पैकेज Control.Monad.Writer
डेटा कंस्ट्रक्टर को निर्यात नहीं करता है Writer
। मुझे लगता है कि यह तब अलग था जब LYAH लिखा गया था।
Ghci में मोनाडविटर टाइपकास्ट का उपयोग करना
इसके बजाय, आप writer
फ़ंक्शन का उपयोग करके लेखक बनाते हैं । उदाहरण के लिए, एक ghci सत्र में मैं कर सकता हूँ
ghci> import Control.Monad.Writer
ghci> let logNumber x = writer (x, ["Got number: " ++ show x])
अब logNumber
एक फ़ंक्शन है जो लेखकों को बनाता है। मैं इसके प्रकार पूछ सकता हूं:
ghci> :t logNumber
logNumber :: (Show a, MonadWriter [String] m) => a -> m a
जो मुझे बताता है कि अनुमानित प्रकार एक फ़ंक्शन नहीं है जो किसी विशेष लेखक को लौटाता है , बल्कि कुछ भी जो MonadWriter
टाइप क्लास को लागू करता है । अब मैं इसका उपयोग कर सकता हूं:
ghci> let multWithLog = do { a <- logNumber 3; b <- logNumber 5; return (a*b) }
:: Writer [String] Int
(इनपुट वास्तव में सभी एक पंक्ति में दर्ज किया गया)। यहाँ मैंने होने का प्रकार निर्दिष्ट multWithLog
किया है Writer [String] Int
। अब मैं इसे चला सकता हूं:
ghci> runWriter multWithLog
(15, ["Got number: 3","Got number: 5"])
और आप देखते हैं कि हम सभी मध्यवर्ती संचालन लॉग करते हैं।
कोड इस तरह क्यों लिखा जाता है?
MonadWriter
टाइप क्लास बनाने के लिए परेशान क्यों ? इसका कारण मोनाड ट्रांसफार्मर के साथ करना है। जैसा कि आपने सही ढंग से महसूस किया है, लागू करने का सबसे सरल तरीका Writer
एक जोड़ी के शीर्ष पर एक नया टाइप रैपर है:
newtype Writer w a = Writer { runWriter :: (a,w) }
आप इसके लिए एक सनक उदाहरण की घोषणा कर सकते हैं, और फिर फ़ंक्शन लिख सकते हैं
tell :: Monoid w => w -> Writer w ()
जो बस इसके इनपुट को लॉग करता है। अब मान लीजिए कि आप एक ऐसा सन्यासी चाहते हैं जिसकी लॉगिंग क्षमताएं हैं, लेकिन यह कुछ और भी करता है - कहते हैं कि यह पर्यावरण से भी पढ़ सकता है। आप इसे लागू करेंगे
type RW r w a = ReaderT r (Writer w a)
अब क्योंकि लेखक ReaderT
monad ट्रांसफॉर्मर के अंदर है , अगर आप आउटपुट लॉग करना चाहते हैं, तो आप इसका उपयोग नहीं कर सकते हैं tell w
(क्योंकि यह केवल अपरिवर्तित लेखकों के साथ काम करता है) लेकिन आपको इसका उपयोग करना होगा lift $ tell w
, जो tell
फ़ंक्शन को "लिफ्ट" करता है ReaderT
ताकि यह एक्सेस कर सके आंतरिक लेखक मोनाद। यदि आप दो लेयर ट्रांसफॉर्मर चाहते थे (कहते हैं कि आप एरर हैंडलिंग को भी जोड़ना चाहते थे) तो आपको उपयोग करने की आवश्यकता होगी lift $ lift $ tell w
। यह जल्दी से बेकार हो जाता है।
इसके बजाय, एक प्रकार के वर्ग को परिभाषित करके हम किसी लेखक के इर्द-गिर्द किसी भी मोनड ट्रांसफॉर्मर रैपर को लेखक के रूप में देख सकते हैं। उदाहरण के लिए,
instance (Monoid w, MonadWriter w m) => MonadWriter w (ReaderT r m)
वह है, अगर w
एक मोनॉयड है, और m
एक है MonadWriter w
, तो ReaderT r m
एक भी है MonadWriter w
। इसका अर्थ है कि हम tell
फ़ंक्शन को सीधे रूपांतरित किए गए मोनड पर उपयोग कर सकते हैं , बिना मोनड ट्रांसफार्मर के माध्यम से स्पष्ट रूप से उठाने के साथ परेशान करने के लिए।