假设我们有一个案例类(可能有十二个以上的成员):
case class Foo(a: Int, b: Char, c: Symbol, d: String)
我们将错误表示为字符串,并为方便起见定义了类型别名:
type ErrorOr[A] = ValidationNel[String, A]
我们也有一些验证结果:
val goodA: ErrorOr[Int] = 1.success
val goodB: ErrorOr[Char] = 'a'.success
val goodC: ErrorOr[Symbol] = 'a.success
val goodD: ErrorOr[String] = "a".success
val badA: ErrorOr[Int] = "x".failNel
val badC: ErrorOr[Symbol] = "y".failNel
现在我们可以写:
val foo = (Foo.apply _).curried
val good: ErrorOr[Foo] = goodD <*> (goodC <*> (goodB <*> (goodA map foo)))
val bad: ErrorOr[Foo] = goodD <*> (badC <*> (goodB <*> (badA map foo)))
这给了我们想要的东西:
scala> println(good)
Success(Foo(1,a,'a,a))
scala> println(bad)
Failure(NonEmptyList(x, y))
在 Haskell 中,这会更漂亮——你只需写:
Foo <$> goodA <*> goodB <*> goodC <*> goodD
不幸的是,Scala 较弱的类型推断要求我们以错误的顺序编写参数。