2011-09-14 21 views
5

यह this प्रश्न का अनुवर्ती है।मुझे इस स्कैला कोड को समझने में सहायता करें: स्कालाज़ आईओ मोनाड और implicits

object io { 
    sealed trait IO[A] { 
    def unsafePerformIO: A 
    } 

    object IO { 
    def apply[A](a: => A): IO[A] = new IO[A] { 
     def unsafePerformIO = a 
    } 
    } 

    implicit val IOMonad = new Monad[IO] { 
    def pure[A](a: => A): IO[A] = IO(a) 
    def bind[A,B](a: IO[A], f: A => IO[B]): IO[B] = IO { 
     implicitly[Monad[Function0]].bind(() => a.unsafePerformIO, 
             (x:A) =>() => f(x).unsafePerformIO)() 
    } 
    } 
} 

यह (मैं एक import io._ निहित है संभालने हूँ) कोड इस तरह प्रयोग किया जाता है

def bufferFile(f: File) = IO { new BufferedReader(new FileReader(f)) } 

def closeReader(r: Reader) = IO { r.close } 

def bracket[A,B,C](init: IO[A], fin: A => IO[B], body: A => IO[C]): IO[C] = for { a <- init 
     c <- body(a) 
     _ <- fin(a) } yield c 

def enumFile[A](f: File, i: IterV[String, A]): IO[IterV[String, A]] = bracket(bufferFile(f), 
      closeReader(_:BufferedReader), 
      enumReader(_:BufferedReader, i)) 

मैं:

यहाँ कोड मैं (यह http://apocalisp.wordpress.com/2010/10/17/scalaz-tutorial-enumeration-based-io-with-iteratees/ से है) को समझने के लिए कोशिश कर रहा हूँ है अब मैं implicit val IOMonad परिभाषा को समझने की कोशिश कर रहा हूं। यहां बताया गया है कि मैं इसे कैसे समझता हूं। यह scalaz.Monad है, इसलिए scalaz.Monad विशेषता के pure और bind अमूर्त मूल्यों को परिभाषित करने की आवश्यकता है।

pure एक मूल्य लेता है और इसे "कंटेनर" प्रकार में निहित मान में बदल देता है। उदाहरण के लिए यह Int ले सकता है और List[Int] लौटा सकता है। यह बहुत आसान लगता है।

bind एक "कंटेनर" प्रकार और एक फ़ंक्शन लेता है जो कंटेनर किसी अन्य प्रकार के प्रकार को मानचित्र करता है। लौटाया जाने वाला मान एक ही कंटेनर प्रकार है, लेकिन अब यह एक नया प्रकार है। एक उदाहरण List[Int] ले जाएगा और List[String] पर उस फ़ंक्शन का उपयोग करके मैपिंग करेगा जो Int s से String s पर नक्शा रखता है। bindmap के समान ही है?

bind का कार्यान्वयन जहां मैं अटक गया हूं। कोड यह रहा:

def bind[A,B](a: IO[A], f: A => IO[B]): IO[B] = IO { 
    implicitly[Monad[Function0]].bind(() => a.unsafePerformIO, 
     (x:A) =>() => f(x).unsafePerformIO)() 
} 

इस परिभाषा IO[A] लेता है और IO[B] के लिए यह एक ऐसा कार्य है जो एक A लेता है और एक IO[B] रिटर्न का उपयोग कर। मुझे ऐसा करने का अनुमान है, इसे flatMap का उपयोग परिणाम को "फ़्लैट" करने के लिए करना है (सही?)।

= IO { ... }

= new IO[A] { 
    def unsafePerformIO = implicitly[Monad[Function0]].bind(() => a.unsafePerformIO, 
     (x:A) =>() => f(x).unsafePerformIO)() 
    } 
} 

मुझे लगता है कि के रूप में ही है?

implicitly विधि एक अंतर्निहित मूल्य (मान, दाएं?) की तलाश करता है जो Monad[Function0] लागू करता है। यह अंतर्निहित परिभाषा कहां से आती है? मुझे लगता है कि यह implicit val IOMonad = new Monad[IO] {...} परिभाषा से है, लेकिन हम अभी उस परिभाषा के अंदर हैं और चीजें थोड़ा गोलाकार हो जाती हैं और मेरा दिमाग एक अनंत लूप में फंस जाता है :)

इसके अलावा, bind पर पहला तर्क (() => a.unsafePerformIO) ऐसा प्रतीत होता है जो कोई पैरामीटर नहीं लेता है और a.unsafePerformIO देता है। मुझे इसे कैसे पढ़ा जाना चाहिए? bind एक कंटेनर प्रकार को अपना पहला तर्क के रूप में लेता है, तो शायद () => a.unsafePerformIO एक कंटेनर प्रकार के लिए हल हो सकता है?

+0

Scalaz वास्तव में एक आईओ इकाई बॉक्स से बाहर अब प्रदान करता है। आयात scalaz.effects._ – Apocalisp

उत्तर

14

IO[A] एक Action एक A, जहां कार्रवाई का परिणाम पर्यावरण पर निर्भर हो सकता है लौटने (कुछ भी अर्थ, चर, फाइल सिस्टम, सिस्टम का समय ... का मान) और कार्रवाई के निष्पादन का प्रतिनिधित्व करने का इरादा है हो सकता है पर्यावरण को भी संशोधित करें। असल में, एक्शन के लिए स्कैला प्रकार Function0 होगा। Function0[A] कॉल किए जाने पर ए को लौटाता है और इसे निश्चित रूप से पर्यावरण पर निर्भर करने और संशोधित करने की अनुमति है। IOFunction0 किसी अन्य नाम के तहत है, लेकिन इसका उद्देश्य अंतर (टैग?) उन Function0 जो अन्य लोगों से पर्यावरण पर निर्भर करता है, जो वास्तव में शुद्ध मूल्य हैं (यदि आप कहते हैं कि एफ एक समारोह है [ए] जो हमेशा एक ही मूल्य देता है, बिना किसी दुष्प्रभाव के, f और उसके बीच कोई अंतर नहीं है परिणाम)। सटीक होने के लिए, यह इतना नहीं है कि आईओ के रूप में टैग की गई फ़ंक्शन को साइड इफेक्ट होना चाहिए। ऐसा इसलिए है कि जिन लोगों को टैग नहीं किया गया उनमें कोई भी नहीं होना चाहिए। नोट IO में अशुद्ध कार्यों को लपेटने के बावजूद पूरी तरह से स्वैच्छिक है, जब आपको Function0 मिलता है तो यह गारंटी नहीं होगी कि यह शुद्ध है। IO का उपयोग निश्चित रूप से स्केल में प्रमुख शैली नहीं है।

शुद्ध एक मूल्य लेता है और इसे "कंटेनर" प्रकार में निहित मान में बदल देता है।

काफी सही, लेकिन "कंटेनर" का अर्थ बहुत सारी चीजें हो सकता है। और शुद्ध द्वारा लौटाया गया जितना संभव हो उतना प्रकाश होना चाहिए, यह वह होना चाहिए जो कोई फर्क नहीं पड़ता। सूची का बिंदु यह है कि उनके पास कई मूल्य हो सकते हैं। शुद्ध द्वारा लौटाया गया एक होना चाहिए। आईओ का मुद्दा यह है कि यह पर्यावरण पर निर्भर करता है और प्रभावित करता है। शुद्ध द्वारा लौटाया गया कोई भी ऐसा काम नहीं करना चाहिए। तो यह वास्तव में शुद्ध Function0() => a है, IO में लपेटा गया है।

बाँध काफी नक्शे के रूप में एक ही

ऐसा नहीं है, बाँध flatMap के समान है। आपके लिखते ही, नक्शा Int से String करने के लिए एक समारोह प्राप्त होगा, लेकिन यहाँ आप Int से List[String]

को समारोह अब है, एक पल के लिए भूल जाते हैं आईओ और विचार क्या बाँध/flatMap एक कार्रवाई के लिए मतलब होगा, उस के लिए है Function0। देखते हैं कि हम बाँध/flatMap करता है, उन वस्तुओं वह कार्रवाई है जो स्ट्रिंग रिटर्न प्राप्त करने के लिए के रूप में, गठबंधन करना चाहिए की अब

val askUserForLineNumber:() => Int = {...} 
val readingLineAt: Int => Function0[String] = {i: Int =>() => ...} 

है, क्या यह होना चाहिए बहुत स्पष्ट है: लाइन नंबर के लिए पाठक से पूछते हैं, पढ़ वह रेखा और इसे वापस लाती है। यही कारण है कि

val askForLineNumberAndReadIt=() => { 
    val lineNumber : Int = askUserForLineNumber() 
    val readingRequiredLine: Function0[String] = readingLineAt(line) 
    val lineContent= readingRequiredLine() 
    lineContent 
} 

अधिक सामान्य रूप से

def bind[A,B](a: Function0[A], f: A => Function0[B]) =() => { 
    val value = a() 
    val nextAction = f(value) 
    val result = nextAction() 
    result 
} 

और कम हो जाएगा:

def bind[A,B](a: Function0[A], f: A => Function0[B]) 
    =() => {f(a())()} 

तो हम जानते हैं कि bind होना चाहिए Function0 के लिए, pure भी स्पष्ट है। हम

object ActionMonad extends Monad[Function0] { 
    def pure[A](a: => A) =() => a 
    def bind[A,B](a:() => A, f: A => Function0[B]) =() => f(a())() 
} 

अब, IO छिपाने में Function0 है। a(), करने के बजाय हमें a.unsafePerformIO करना होगा। और एक के बजाय () => body परिभाषित करने के लिए, हम लिख IO {body} तो वहाँ

object IOMonad extends Monad[IO] { 
    def pure[A](a: => A) = IO {a} 
    def bind[A,B](a: IO[A], f: A => IO[B]) = IO {f(a.unsafePerformIO).unsafePerformIO} 
} 

मेरी नजर में, कि काफी अच्छा होगा हो सकता है। लेकिन वास्तव में यह एक्शनमोनाड दोहराता है। आपके द्वारा संदर्भित कोड में बिंदु उस से बचने के लिए है और इसके बजाय Function0 के लिए क्या किया जाता है इसका पुन: उपयोग करना है।एक IO से Function0 (() => io.unsafePerformIo के साथ) Function0 से IO (IO { action() } के साथ) से आसानी से चला जाता है। यदि आपके पास एफ: ए => आईओ [बी] है, तो आप इसे पर भी बदल सकते हैं, बस IO से Function0 रूपांतरण के साथ लिखकर, (x: A) => f(x).unsafePerformIO

क्या आईओ के बाँध में यहाँ होता है:

  1. () => a.unsafePerformIO: एक Function0
  2. (x:A) =>() => f(x).unsafePerformIO) में a बारी: एक ए में f बारी =>Function0[B]
  3. परोक्ष
  4. [इकाई [Function0]] : Function0 के लिए डिफ़ॉल्ट मोनैड प्राप्त करें, ActionMonad
  5. bind(...):लागू करें तर्क a और f को Function0 इकाई कि सिर्फ Function0
  6. को बदल दिया गया है enclosing IO{...} की 3,103,210: IO वापस करने के लिए परिणाम कन्वर्ट।

(सुनिश्चित नहीं हैं कि मुझे पसंद है यह बहुत)

+0

बहुत बहुत धन्यवाद! मैं इसे 5x –

+0

के बारे में पढ़ रहा हूं, मैं इसे कैसे पढ़ूंगा 'वैल रीडिंगलाइन: Int => Function0 [स्ट्रिंग] = Int => स्ट्रिंग {i: Int =>() => ...} '? जैसे "readLineAt" एक ऐसा मान है जो एक तरीका देता है जो एक इंट लेता है और एक फंक्शन 0 [स्ट्रिंग] देता है। मैं इसे दूर कर सकता हूं, लेकिन मुझे कार्यान्वयन के बारे में निश्चित नहीं है। 'int => स्ट्रिंग' किसी प्रकार का अनाम फ़ंक्शन है? –

+0

क्षमा करें, गलत टाइप/गलत/गलत टाइप किया गया। फिक्स्ड –

संबंधित मुद्दे