Scalaz 验证与应用函子 |@|不工作 [英] Scalaz Validation with applicative functor |@| not working
问题描述
我正在尝试在我的应用中使用 Scalaz 7 验证.但是,我在使用 |@|
应用函子来合并我的失败时遇到了问题.这是我的代码:
type ValidationResult = ValidationNel[String, Unit]def validate[A: ClassTag](instance: A, fieldNames: Option[Seq[String]] = None): ValidationResult = {val 字段 = classTag[A].runtimeClass.getDeclaredFieldsval fieldSubset = fieldNames 匹配 {case Some(names) =>fields.filter { 字段 =>names.contains(field.getName) }情况无=>领域}fieldSubset.map {字段 =>field.getAnnotations.toSeq.map {field.setAccessible(真)val (name, value) = (field.getName, field.get(instance))field.setAccessible(false)注释 =>注释匹配{case min: Min =>minValidate(name, value, min.value())表壳尺寸:尺寸 =>sizeValidate(name, value, size.min(), size.max())}}}.flatten[ValidationResult].foldLeft(().successNel[String])(_ |@| _)}
minValidate
和 sizeValidate
函数只返回 ValidationResults
.
问题是,这段代码无法编译.错误信息是:
类型不匹配,预期 F0.type#M[NotInferedB],实际:ValidationResult
我不知道这意味着什么……我需要给 Scala 更多类型信息吗?
我想要完成的是,如果所有字段都是 successNel
s,则返回该字段,否则返回所有 failureNel
s 的组合.>
|@|
与之前版本的 Scalaz 相比有变化吗?因为即使我做了类似的事情:
().successNel |@|().successNel
我遇到了同样的错误.
更新
我开始浏览 Scalaz 源代码,发现 +++
似乎可以满足我的需求.
+++
和 |@|
有什么区别?
Scalaz 的应用构建器语法 (|@|
) 为您提供了一种将函数提升"为应用函子的方法.假设我们有以下结果,例如:
val xs: ValidationNel[String, List[Int]] = "Error!".failNelval ys: ValidationNel[String, List[Int]] = List(1, 2, 3).successval zs: ValidationNel[String, List[Int]] = List(4, 5).success
我们可以像这样将列表连接函数(++
)提升到Validation
中:
scala>println((ys |@| zs)(_ ++ _))成功(列表(1、2、3、4、5))标度>println((xs |@| ys)(_ ++ _))失败(非空列表(错误!))标度>println((xs |@| xs)(_ ++ _))失败(非空列表(错误!,错误!))
这种语法有点奇怪——例如,它与你在 Haskell 中将函数提升到应用函子的方式非常不同,它的设计主要是为了胜过 Scala 相当愚蠢的类型推断系统.请参阅我的回答或此处的博文进行更多讨论.
奇怪的部分是 xs |@|ys
本身并没有真正的意义——它本质上是一个参数列表,它等待被应用到一个函数中,该函数将提升到它的应用函子中并应用于自身.
Validation
上的 +++
是一种更简单的生物——它只是类型的 Semigroup
实例的加法运算(请注意,您可以在此处等效地使用 Scalaz 的半群运算符 |+|
代替 +++
).你给它两个具有匹配半群类型的 Validation
结果,它给你另一个 Validation
——不是一些糟糕的 ApplyOps
东西.
作为旁注,在这种情况下,Validation
的半群的加法运算与右侧提升到 Validation
的半群运算相同:
scala>(xs |+| ys) == (xs |@| ys)(_ |+| _)res3:布尔值 = 真
然而,情况并非总是如此(例如,它不适用于 \/
,其中半群累积错误但应用函子不会).
I'm trying to use Scalaz 7 Validation in my app. However, I'm having an issue getting the |@|
applicative functor to coalesce my failures. Here's the code I have:
type ValidationResult = ValidationNel[String, Unit]
def validate[A: ClassTag](instance: A, fieldNames: Option[Seq[String]] = None): ValidationResult = {
val fields = classTag[A].runtimeClass.getDeclaredFields
val fieldSubset = fieldNames match {
case Some(names) => fields.filter { field => names.contains(field.getName) }
case None => fields
}
fieldSubset.map {
field => field.getAnnotations.toSeq.map {
field.setAccessible(true)
val (name, value) = (field.getName, field.get(instance))
field.setAccessible(false)
annotation => annotation match {
case min: Min => minValidate(name, value, min.value())
case size: Size => sizeValidate(name, value, size.min(), size.max())
}
}
}.flatten[ValidationResult].foldLeft(().successNel[String])(_ |@| _)
}
The minValidate
and sizeValidate
functions just return ValidationResults
.
The problem is, this code won't compile. The error message is:
Type mismatch, expected F0.type#M[NotInferedB], actual: ValidationResult
I have no idea what that means... do I need to give Scala more type info?
What I'm trying to accomplish is, if all fields are successNel
s, then return that, otherwise, return a combination of all the failureNel
s.
Has |@|
changed since previous version of Scalaz? Because even if I do something like:
().successNel |@| ().successNel
I get the same error.
Update
I started poking around the Scalaz source and I found the +++
which seems to do what I want.
What's the difference between +++
and |@|
?
Scalaz's applicative builder syntax (|@|
) gives you a way of "lifting" functions into an applicative functor. Suppose we have the following results, for example:
val xs: ValidationNel[String, List[Int]] = "Error!".failNel
val ys: ValidationNel[String, List[Int]] = List(1, 2, 3).success
val zs: ValidationNel[String, List[Int]] = List(4, 5).success
We can lift the list concatenation function (++
) into the Validation
like this:
scala> println((ys |@| zs)(_ ++ _))
Success(List(1, 2, 3, 4, 5))
scala> println((xs |@| ys)(_ ++ _))
Failure(NonEmptyList(Error!))
scala> println((xs |@| xs)(_ ++ _))
Failure(NonEmptyList(Error!, Error!))
This syntax is a little weird—it's very unlike how you lift functions into an applicative functor in Haskell, for example, and is designed this way primarily to outsmart Scala's fairly stupid type inference system. See my answer here or blog post here for more discussion.
One part of the weirdness is that xs |@| ys
doesn't really mean anything on its own—it's essentially an argument list that's waiting to be applied to a function that it will lift into its applicative functor and apply to itself.
The +++
on Validation
is a much simpler kind of creature—it's just the addition operation for the Semigroup
instance for the type (note that you could equivalently use Scalaz's semigroup operator |+|
here in place of +++
). You give it two Validation
results with matching semigroup types and it gives you another Validation
—not some awful ApplyOps
thing.
As a side note, in this case the addition operation for Validation
's semigroup is the same as the semigroup operation for the right side lifted into the Validation
:
scala> (xs |+| ys) == (xs |@| ys)(_ |+| _)
res3: Boolean = true
This won't always be the case, however (it's not for \/
, for example, where the semigroup accumulates errors but the applicative functor doesn't).
这篇关于Scalaz 验证与应用函子 |@|不工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!