【问题标题】:Why doesn't Scala infer the right type of ValidationNel?为什么 Scala 不能推断出正确的 ValidationNel 类型?
【发布时间】:2017-05-18 06:16:04
【问题描述】:
    def isEven(x: Int) = 
          if (x % 2 == 0) x.successNel else "not even".failureNel

以上代码中isEven的返回类型推断为scalaz.Validation[scalaz.NonEmptyList[_ <: String],Int]

虽然明确指定返回类型有效,

    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)) {_ + _ + _ + _}

只有第二种变体(具有显式返回类型)有效。

【问题讨论】:

    标签: scala type-inference scalaz


    【解决方案1】:

    scalaz.NonEmptyList[String]scalaz.NonEmptyList[_ <: String] 的子类型,即scalaz.ValidationNEL[String,Int]scalaz.Validation[scalaz.NonEmptyList[_ <: String],Int] 的子类型。在这种情况下,编译器只会为您提供更精确的类型...

    编辑: raisin bread 运算符需要一个 Applicative 实例。在这种情况下,我认为 scalaz 为ValidationNel[String, ?] 提供了这样的实例,但不是为您的isEven 推断出的更精确的类型。

    可以通过Option 观察到更简单的表现形式:

    val some = Some(1)
    val none = None
    (some |@| none)(_ + _) // Cannot find implicit Applicative[Some] :(
    

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

    【讨论】:

    • 好吧,有道理。我已经编辑了我的问题以添加一个表达式。你能回答为什么在使用 isEven 的第一个变体时它不能编译吗?
    • 好吧,如果我做对了,为 M[T] 定义的隐式对 M[+T] 或 M[A <: t>
    • 如果您要求implicit Applicative[T]Applicative 是不变的),编译器会按照您的预期查找Applicative[T]
    【解决方案2】:

    我将尝试解释更多细节:

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

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

    首先,isEven(4) 尝试使用|@| 运算符将implicitly 类型从Validation[+E, +A] 类型转换为ApplyOps[F0.M,F0.A]type。作为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]变量。对于UnApplyimplicits,它在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],编译器找不到适合该类型 ValidaitonApplicative 隐式.

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

      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 unbind L,所以现在这是类型变体。只是为了好玩;)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-31
      • 1970-01-01
      • 2018-06-04
      • 2021-11-10
      • 2012-01-15
      • 2021-12-21
      • 1970-01-01
      • 2011-10-16
      相关资源
      最近更新 更多