为什么 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
类型。所以它会尝试寻找 Validation
到 Applicative
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]
所以问题是:F
是type 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
,所以现在这是类型变体。只是为了好玩;).
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
类型。所以它会尝试寻找 Validation
到 Applicative
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]
所以问题是:F
是type 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
,所以现在这是类型变体。只是为了好玩;).