2012-02-23 12 views
11

के साथ आवेदकों को फ़ंक्शन कैसे लिखें स्केलज़ 6 सीखते समय, मैं टाइप-सुरक्षित पाठकों को सत्यापन वापस करने की कोशिश कर रहा हूं। यह मेरा नया प्रकार हैं:स्केलज़

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

और मैं (*) दो ints और तार के लिए नक्शे-पाठकों बनाने कार्यों:

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(_,_) } 

(*) पूर्ण और में runnable कार्यान्वयन: 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

मैं एक जवाब बाद में पोस्ट करेंगे, लेकिन एक संकेत के रूप में, याद रखें कि 'आवेदक [जी]' और 'आवेदक [एफ] 'का मतलब है' आवेदक [[x] एफ [जी [एक्स]]'। स्कालज़ 7 में, इस तथ्य के साक्षी 'आवेदक # लिखें' गवाह। '| @ |' वाक्यविन्यास का उपयोग करने के बजाय, शुरू करने के लिए टाइप क्लास के साथ सीधे काम करें। – retronym

+0

धन्यवाद, लेकिन मुझे अभी भी यह नहीं मिला है, इसलिए मैं आपके उत्तर का इंतजार करूंगा। ध्यान दें कि मैं स्केलज़ 6 का उपयोग कर रहा हूं (प्रश्न अपडेट किया गया)। – paradigmatic

+0

@paradigmatic क्या आपने स्पष्ट रूप से 'लागू' का उपयोग करने का प्रयास किया है?'(नाम | @ | आयु) की तरह {लड़का (_, _)}' लागू करें? –

उत्तर

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

+0

@paradigmatic, मुझे लगता है कि एक प्रकार उपनाम इसे सबसे अधिक पठनीय बनाता है। इसके अलावा मैं इसे दो कारणों से एक काल्पनिक अंतर्निहित रूपांतरण के लिए पसंद करता हूं: (1) यह मुझे '(लड़का (_, _)) का प्रकार बताता है। लिफ्ट [वीएनईएलएसआरटी]' लैम्ब्डा - मैं आपके प्रकार को समझ नहीं पाया संस्करण। (2) इसी कारण से मैं 'Int' को' विकल्प [int] में स्पष्ट रूप से बदलना नहीं चाहता हूं, मुझे लगता है कि दीपक को पूरी तरह परिवर्तित करने से टाइप सिस्टम का उपयोग करने से दूर हो जाता है। – huynhjl

+0

@huynjl मुझे टाइप उपनाम पसंद है और मैं आपका बिंदु देखता हूं। धन्यवाद। – paradigmatic

2

ध्यान दें कि Reader और Validation (एक सेमिग्रुप ई के साथ) दोनों लागू हैं, उनकी रचना भी लागू है। 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

अस्पष्ट निहित संकल्प के बारे में, मैंने http://stackoverflow.com/questions/11913128/scalaz-7-why-using-type-alias-results-in-ambigous-typeclass-resolution-for-rea – ron

+0

दुर्भाग्यवश आपको खोला गया स्कोप में पहचान उदाहरण प्राप्त करने के लिए 'scalaz.Id._' आयात करने के लिए। लेकिन फिर आप 'रीडर [इंट, ए]' का उपयोग करने में सक्षम होना चाहिए। – retronym