Scalaz 验证与应用函子 |@|不工作 [英] Scalaz Validation with applicative functor |@| not working

查看:54
本文介绍了Scalaz 验证与应用函子 |@|不工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在我的应用中使用 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])(_ |@| _)}

minValidatesizeValidate 函数只返回 ValidationResults.

问题是,这段代码无法编译.错误信息是:

类型不匹配,预期 F0.type#M[NotInferedB],实际:ValidationResult

我不知道这意味着什么……我需要给 Scala 更多类型信息吗?

我想要完成的是,如果所有字段都是 successNels,则返回该字段,否则返回所有 failureNels 的组合.>

|@| 与之前版本的 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 successNels, then return that, otherwise, return a combination of all the failureNels.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆