2010-07-19 7 views
8

साथ मैं इस तरह scalaz साथ रीडर इकाई परिभाषित करने की कोशिश:रीडर इकाई Scalaz

import scalaz._ 
import Scalaz._ 

final class Reader[E,A](private[Reader] val runReader: E => A) 

object Reader { 
    def apply[E,A](f: E => A) = new Reader[E,A](f) 
    def env[E]: Reader[E,E] = Reader(identity _) 

    implicit def ReaderMonad[E] = new Monad[PartialApply1Of2[Reader,E]#Apply] { 
    def pure[A](a: => A) = Reader(_ => a) 

    def bind[A,B](m: Reader[E,A], k: A => Reader[E,B]) = 
     Reader(e => k(m.runReader(e)).runReader(e)) 
    } 
} 


object Test { 
    import Reader._ 

    class Env(val s: String) 

    def post(s: String): Reader[Env, Option[String]] = 
    env >>= (e => if (e.s == s) some(s).pure else none.pure) 
} 

लेकिन मैं एक संकलक त्रुटि मिलती है:

reader.scala:27: reassignment to val 
    env >>= (e => if (e.s == s) some(s).pure else none.pure) 
     ^

कि क्यों है?

धन्यवाद, लेवी

उत्तर

16

यह त्रुटि भी स्काला के मानकों के अनुसार, काफी अपारदर्शी है। = के साथ समाप्त होने वाले विधि नामों का विशेष रूप से इलाज किया जाता है - उन्हें पहले सामान्य पहचानकर्ता के रूप में माना जाता है, और विफल होने पर, उन्हें स्वयं असाइनमेंट में विस्तारित किया जाता है।

scala> def env[A] = 0 
env: [A]Int 

scala> env >>= 0 
<console>:7: error: reassignment to val 
     env >>= 0 
     ^

scala> env = env >> 0 
<console>:6: error: reassignment to val 
     env = env >> 0 
     ^

आप अपने कार्यक्रम के वाक्यात्मक व्याख्या लेकर असमंजस में हैं, तो यह scalac -Xprint:parser चलाने के लिए देखने के लिए क्या हो रहा है एक अच्छा विचार है। इसी तरह, आप कार्यक्रम परिवर्तन के बाद के चरणों को देखने के लिए -Xprint:typer या -Xprint:jvm का उपयोग कर सकते हैं।

तो, आप अपने Reader पर >>= पर कैसे कॉल करते हैं? सबसे पहले, आपको स्पष्ट रूप से प्रकार तर्क Env से env को पारित करने की आवश्यकता होगी। परिणामी Reader[Env, Env] को MA[M[_], A] में परिवर्तित किया जाना चाहिए। सरल प्रकार के रचनाकारों के लिए, अंतर्निहित रूपांतरण MAs#ma पर्याप्त होगा। हालांकि दो परम प्रकार के कन्स्ट्रक्टर Reader आंशिक रूप से लागू किए जाने चाहिए - इसका मतलब है कि इसका अनुमान नहीं लगाया जा सकता है और इसके बजाय आपको एक विशिष्ट निहित रूपांतरण प्रदान करना होगा।

अगर एड्रियान को कभी भी अतिरिक्त दोपहर implement higher-order unification for type constructor inference पर स्थिति मिलती है तो स्थिति में काफी सुधार होगा। :)

तब तक, आपका कोड यहां है। कुछ और टिप्पणियां इनलाइन हैं।

import scalaz._ 
import Scalaz._ 

final class Reader[E, A](private[Reader] val runReader: E => A) 

object Reader { 
    def apply[E, A](f: E => A) = new Reader[E, A](f) 

    def env[E]: Reader[E, E] = Reader(identity _) 

    implicit def ReaderMonad[E]: Monad[PartialApply1Of2[Reader, E]#Apply] = new Monad[PartialApply1Of2[Reader, E]#Apply] { 
    def pure[A](a: => A) = Reader(_ => a) 

    def bind[A, B](m: Reader[E, A], k: A => Reader[E, B]) = 
     Reader(e => k(m.runReader(e)).runReader(e)) 
    } 

    // No Higher Order Unification in Scala, so we need partially applied type constructors cannot be inferred. 
    // That's the main reason for defining function in Scalaz on MA, we can create one implicit conversion 
    // to extract the partially applied type constructor in the type parameter `M` of `MA[M[_], A]`. 
    // 
    // I'm in the habit of explicitly annotating the return types of implicit defs, it's not strictly necessary 
    // but there are a few corner cases it pays to avoid. 
    implicit def ReaderMA[E, A](r: Reader[E, A]): MA[PartialApply1Of2[Reader, E]#Apply, A] = ma[PartialApply1Of2[Reader, E]#Apply, A](r) 
} 


object Test { 
    import Reader._ 

    class Env(val s: String) 

    def post(s: String): Reader[Env, Option[String]] = 
    // Need to pass the type arg `Env` explicitly here. 
    env[Env] >>= {e => 
     // Intermediate value and type annotation not needed, just here for clarity. 
     val o: Option[String] = (e.s === s).guard[Option](s) 
     // Again, the partially applied type constructor can't be inferred, so we have to explicitly pass it. 
     o.pure[PartialApply1Of2[Reader, Env]#Apply] 
    } 
} 
+2

धन्यवाद। वह चाल है। मुझे कबूल करना है कि स्कैला वास्तव में निराशाजनक है जब मैं वास्तव में इसे कार्यात्मक भाषा के रूप में उपयोग करने की कोशिश करता हूं क्योंकि यह मुझे एक विशाल हैक की तरह लगता है। –

+4

आप हास्केल से आ रहे हैं, मुझे लगता है। स्कैला हिंडली-मिलनर अनुमान से प्रतिस्पर्धा नहीं कर सकती है, शुद्धता लागू नहीं करती है, और डिफ़ॉल्ट रूप से सख्त है। इसमें जेवीएम इंटरऑप है, अंतर्निहित पैरा कुछ चीजों को एन्कोड कर सकते हैं टाइप क्लास के साथ मुश्किल हैं। – retronym