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)) {_ + _ + _ + _}
并且只有第二个变体(具有显式返回类型)有效。
答案 0 :(得分:3)
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很好地配合。我建议遵循良好实践并使用显式类型注释所有公共方法和含义。
答案 1 :(得分:1)
我将尝试解释有关的更多细节:
为什么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
变量。在那里,我们还发现它正在寻找另一个含义 TC0: TC[M0[F0, ?]]
变量。如前ToApplyOpsUnapply
,TC0
将Apply
类型,也可以是Applicative
类型。所以它会尝试查找Validation
到Applicative
暗示,它位于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]
问题是:F
类型不变,与Option[+A]
不一样,编译器找不到合适的适用暗示输入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
}
在那里,我们只是从L
取消绑定 Semigroup
,所以现在这是类型变体。只是为了好玩;)。