2012-02-23 63 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

相關問題