2012-02-23 24 views
11

在学习时Scalaz 6,我正在尝试编写返回验证的类型安全的读取程序。这里是我的新类型:如何使用scalaz编写应用程序的功能

type ValidReader[S,X] = (S) => Validation[NonEmptyList[String],X] 
type MapReader[X] = ValidReader[Map[String,String],X] 

,我有两个函数创建地图阅读器的整数和字符串(*):

def readInt(k: String): MapReader[Int] = ... 
def readString(k: String): MapReader[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(_,_) } 

(*)全部和运行的执行:https://gist.github.com/1891147


更新:以下是编译器错误消息试图行了,当上面或丹尼尔建议,我得到:

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

我以后会发布一个答案,但作为一个提示,请记住, 'Applicative [G]'和'Applicative [F]'意味着'Applicative [[x] F [G [x]]'。在scalaz 7中,'Applicative#compose'证明了这个事实。首先直接使用类型类,而不是使用'| @''语法。 – retronym 2012-02-23 07:58:38

+0

谢谢,但我仍然不明白,所以我会等待你的回答。请注意,我正在使用scalaz 6(问题已更新)。 – paradigmatic 2012-02-23 08:05:25

+0

@paradigmatic你有没有试过明确使用'apply'?像'(name | @ | age)应用{Boy(_,_)}'? – 2012-02-23 13:59:01

回答

5

这个怎么样?

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]) => ? 

所以我刚刚解禁Boy.apply采取必要的类型。

+0

谢谢。我没有考虑解除。然而,我不确定拉姆达式汤比使用两个应用建造者级联更可读。您知道是否有办法通过隐式转换提升构造函数? – paradigmatic 2012-02-24 05:37:27

+0

@paradigmatic,我认为一个类型别名使它最具可读性。另外我更喜欢这个假设的隐式转换,原因有两个:(1)它告诉我'(男孩(_,_))的类型。lift [VNELStr]'lambda - 我无法弄清楚你的类型版。 (2)出于同样的原因,我不想隐式地将'Int'更改为'Option [Int]'我认为隐式地转换lampda会使用类型系统。 – huynhjl 2012-02-24 15:22:34

+0

@huynjl我喜欢类型别名,我明白你的观点。谢谢。 – paradigmatic 2012-02-24 18:03:51

2

请注意,由于ReaderValidation(带有半群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")) 
+0

关于ambigous隐式的决议,我打开http://stackoverflow.com/questions/11913128/scalaz-7-why-using-type-alias-results-in-ambigous-typeclass-resolution-for-rea – ron 2012-08-11 08:17:30

+0

不幸的是,你需要导入'scalaz.Id._'来获取范围内的身份实例。但是,你应该可以使用'Reader [Int,A]'。 – retronym 2012-08-11 08:37:47

相关问题