为什么 Scala 不推断正确的 ValidationNel 类型?

Why doesn't Scala infer the right type of ValidationNel?

    def isEven(x: Int) = 
          if (x % 2 == 0) x.successNel else "not even".failureNel

在上面的代码中,isEven的return类型被推断为scalaz.Validation[scalaz.NonEmptyList[_ <: String],Int]

虽然显式指定 return 类型有效,

    def isEven(x: Int): ValidationNel[String, Int] = 
          if (x % 2 == 0) x.successNel else "not even".failureNel

有人可以解释一下这里的幕后情况吗?这是 Scala 类型推断系统的限制吗?

我正在尝试在以下表达式中使用上述函数,

(isEven(4) |@| isEven(6) |@| isEven(8) |@| isEven(10)) {_ + _ + _ + _}

并且只有第二种变体(具有显式 return 类型)有效。

scalaz.NonEmptyList[String]scalaz.NonEmptyList[_ <: String]的子类型,意思是scalaz.ValidationNEL[String,Int]scalaz.Validation[scalaz.NonEmptyList[_ <: String],Int]的子类型。在这种情况下,编译器只会给你一个更精确的类型...

编辑: 葡萄干面包 运算符需要一个 Applicative 实例。在这种情况下,我假设 scalaz 为 ValidationNel[String, ?] 提供了这样的实例,但没有为您的 isEven.

推断出更精确的类型

可以通过 Option:

观察到更简单的表现形式
val some = Some(1)
val none = None
(some |@| none)(_ + _) // Cannot find implicit Applicative[Some] :(

您可以将其视为局部类型推断的交叉限制。您也可以说 FP 并不总是与 OO 配合得很好。我建议遵循良好做法,并使用显式类型注释所有 public 方法和隐式。

我将尝试解释有关以下内容的更多详细信息:

为什么 scalaz.Validation[scalaz.NonEmptyList[_ <: String],Int] 这不能与 |@| 一起使用?

isEven(4) |@| isEven(6) ...

首先,isEven(4) 试图 implicitly 使用 |@| 运算符将其从 Validation[+E, +A] 类型转换为 ApplyOps[F0.M,F0.A] 类型。作为 ApplySyntax 中的 implicit 方法:

  implicit def ToApplyOpsUnapply[FA](v: FA)(implicit F0: Unapply[Apply, FA]) =
    new ApplyOps[F0.M,F0.A](F0(v))(F0.TC)

如上代码,对于这个implicit转换,我们还需要一个隐式 F0: Unapply[Apply, FA]变量。对于 UnApply implicits,它位于 UnApply:

  implicit def unapplyMFA[TC[_[_]], M0[_[_], _], F0[_], A0](implicit TC0: TC[M0[F0, ?]]): Unapply[TC, M0[F0, A0]] {
    type M[X] = M0[F0, X]
    type A = A0
  } =
    new Unapply[TC, M0[F0, A0]] {
      type M[X] = M0[F0, X]
      type A = A0
      def TC = TC0
      def leibniz = refl
    }

作为Validation类型,它使用unapplyMFA implicits变量。在那里我们还发现它正在寻找另一个 implicits TC0: TC[M0[F0, ?]] 变量。如前面的ToApplyOpsUnapply,里面的TC0会是Apply类型,那也可以是Applicative类型。所以它会尝试寻找 ValidationApplicative implicits,它在 Validation.scala

  implicit def ValidationApplicative[L: Semigroup]: Applicative[Validation[L, ?]] =
    new Applicative[Validation[L, ?]] {
      override def map[A, B](fa: Validation[L, A])(f: A => B) =
        fa map f

      def point[A](a: => A) =
        Success(a)

      def ap[A, B](fa: => Validation[L, A])(f: => Validation[L, A => B]) =
        fa ap f
    }

对于 Semigroup 定义:

trait Semigroup[F]  { self => //F is invariant, unlike Option[+A]

所以问题是:Ftype invariant,不像Option[+A],编译器找不到合适的Applicative 隐含此类型 Validaiton

有一个关于如何为此启用类型变体的小演示:

  def isEven(x: Int): Validation[NonEmptyList[_ <: String], Int] =
    if (x % 2 == 0) x.successNel else "not even".failureNel


  val res: String = foo(isEven(4))

  def foo[FA](v: FA)(implicit F0: Unapply[Apply, FA]): String = "foo bar"

  implicit def ValidationApplicative[L]: Applicative[Validation[L, ?]] =
    new Applicative[Validation[L, ?]] {
      override def map[A, B](fa: Validation[L, A])(f: A => B) =
        fa map f

      def point[A](a: => A) =
        Success(a)

      def ap[A, B](fa: => Validation[L, A])(f: => Validation[L, A => B]) =
      //fa ap f
        null
    }

在那里我们只是 Semigroup 解除绑定 L,所以现在这是类型变体。只是为了好玩;).