Validation 如何用scalaz为应用程序编写函数

Validation 如何用scalaz为应用程序编写函数,validation,scala,scalaz,applicative,Validation,Scala,Scalaz,Applicative,在学习Scalaz6时,我试图编写返回验证的类型安全读取器。以下是我的新类型: type ValidReader[S,X] = (S) => Validation[NonEmptyList[String],X] type MapReader[X] = ValidReader[Map[String,String],X] 我有两个函数为int和string(*)创建地图读取器: 根据以下地图: val data = Map( "name" -> "Paul", "age" ->

在学习Scalaz6时,我试图编写返回验证的类型安全读取器。以下是我的新类型:

type ValidReader[S,X] = (S) => Validation[NonEmptyList[String],X]
type MapReader[X] = ValidReader[Map[String,String],X]
我有两个函数为int和string(*)创建地图读取器:

根据以下地图:

val data = Map( "name" -> "Paul", "age" -> "8" )
我可以编写两个阅读器来检索姓名和年龄:

val name = readString( "name" )
val age = readInt( "age" )

println( name(data) ) //=> Success("Paul")
println( age(data) )  //=> Success(8)
一切正常,但现在我想编写两个阅读器来构建一个
Boy
实例:

case class Boy( name: String, age: Int )
我的最佳选择是:

  val boy = ( name |@| age ) {
    (n,a) => ( n |@| a ) { Boy(_,_) }
  }
  println( boy(data) ) //=> Success(Boy(Paul,8))
它的工作原理与预期一样,但是对于两个级别的应用程序构建器来说,表达式很难使用。有没有办法让下面的语法起作用

  val boy = ( name |@| age ) { Boy(_,_) }
(*)完整且可运行的实施:


更新:以下是我在尝试上面这行或Daniel建议时收到的编译器错误消息:

[error] ***/MapReader.scala:114: type mismatch;
[error]  found   : scalaz.Validation[scalaz.NonEmptyList[String],String]
[error]  required: String
[error]   val boy = ( name |@| age ) { Boy(_,_) }
[error]                                    ^
这个怎么样

val boy = (name |@| age) {
  (Boy.apply _).lift[({type V[X]=ValidationNEL[String,X]})#V]
}
或使用类型别名:

type VNELStr[X] = ValidationNEL[String,X]

val boy = (name |@| age) apply (Boy(_, _)).lift[VNELStr]
这是基于控制台上的以下错误消息:

scala> name |@| age apply Boy.apply
<console>:22: error: type mismatch;
 found   : (String, Int) => MapReader.Boy
 required: (scalaz.Validation[scalaz.NonEmptyList[String],String], 
            scalaz.Validation[scalaz.NonEmptyList[String],Int]) => ?
scala>name|@| age apply Boy.apply
:22:错误:类型不匹配;
找到:(字符串,Int)=>MapReader.Boy
必需:(scalaz.Validation[scalaz.NonEmptyList[String],String],
scalaz.Validation[scalaz.NonEmptyList[String],Int])=>?

因此,我刚刚举起了
男孩。应用
来获取所需的类型。

注意,由于
读取器
验证
(带有半群E)都是适用的,所以它们的组合也是适用的。使用scalaz 7,这可以表示为:

import scalaz.Reader
import scalaz.Reader.{apply => toReader}
import scalaz.{Validation, ValidationNEL, Applicative, Kleisli, NonEmptyList}

//type IntReader[A] = Reader[Int, A] // has some ambigous implicit resolution problem
type IntReader[A] = Kleisli[scalaz.IdInstances#Id, Int, A]
type ValNEL[A] = ValidationNEL[Throwable, A]

val app = Applicative[IntReader].compose[ValNEL]
现在,我们可以在组合的应用程序上使用单个
@
操作:

val f1 = toReader((x: Int) => Validation.success[NonEmptyList[Throwable], String](x.toString))
val f2 = toReader((x: Int) => Validation.success[NonEmptyList[Throwable], String]((x+1).toString))

val f3 = app.map2(f1, f2)(_ + ":" + _)

f3.run(5) should be_==(Validation.success("5:6"))

稍后我将发布一个答案,但作为提示,请记住
Applicative[G]
Applicative[F]
意味着
Applicative[[x]F[G[x]]
。在scalaz 7中,
Applicative#compose
见证了这一事实。直接使用要开始的类型类,而不是使用
@|
语法。谢谢,但我仍然不明白,所以我将等待您的回答。请注意,我使用的是scalaz 6(问题更新)@paradigmatic您是否尝试过显式地使用
apply
?例如
(name |@age)apply{Boy(|,|)}
?@DanielC.Sobral是的,它也不起作用(我得到相同的编译错误).@DanielC.Sobral我更新了问题以包含编译器错误消息。谢谢。我没有考虑提升。但是,我不确定lambda类型soup是否比级联使用两个应用程序构建器更具可读性。你知道是否有办法通过隐式转换提升构造函数吗?@paradigmatic,我认为是al类型ias使它最具可读性。另外,我更喜欢它而不是假设的隐式转换,原因有两个:(1)它告诉我
(Boy(,)).lift[VNELStr]
lambda的类型-我无法在您的版本中找出类型。(2)出于同样的原因,我不想隐式地将
Int
更改为
选项[Int]
我认为隐式转换lampda会减少对类型系统的使用。在scalaz 7中,这个#lift方法似乎出现在Function2上,而不是Function3和更高版本上?这是故意的吗?关于模糊隐式解析,我很遗憾地打开了,您需要导入
scalaz.Id.。\u
以获取sco中的标识实例但是你应该能够使用
Reader[Int,A]
val f1 = toReader((x: Int) => Validation.success[NonEmptyList[Throwable], String](x.toString))
val f2 = toReader((x: Int) => Validation.success[NonEmptyList[Throwable], String]((x+1).toString))

val f3 = app.map2(f1, f2)(_ + ":" + _)

f3.run(5) should be_==(Validation.success("5:6"))