कैसे नियंत्रण के साथ खेलने के लिए।


96

मैं कार्यात्मक प्रोग्रामिंग में नया हूं और हाल ही में लर्न यू हास्केल में सीख रहा हूं, लेकिन जब मैं इस अध्याय से गुजरा , तो मैं नीचे दिए गए कार्यक्रम के साथ फंस गया:

import Control.Monad.Writer  

logNumber :: Int -> Writer [String] Int  
logNumber x = Writer (x, ["Got number: " ++ show x])  

multWithLog :: Writer [String] Int  
multWithLog = do  
    a <- logNumber 3  
    b <- logNumber 5  
    return (a*b)

मैंने इन पंक्तियों को एक .hs फ़ाइल में सहेजा है, लेकिन इसे अपने ghci में आयात करने में विफल रहा, जिसने शिकायत की:

more1.hs:4:15:
    Not in scope: data constructor `Writer'
    Perhaps you meant `WriterT' (imported from Control.Monad.Writer)
Failed, modules loaded: none.

मैंने ": info" कमांड द्वारा प्रकार की जांच की:

Prelude Control.Monad.Writer> :info Writer
type Writer w = WriterT w Data.Functor.Identity.Identity
               -- Defined in `Control.Monad.Trans.Writer.Lazy'

मेरे दृष्टिकोण से, यह "न्यूटाइप राइटर वा ..." जैसा कुछ होना चाहिए था, इसलिए मैं उलझन में हूं कि डेटा कंस्ट्रक्टर को कैसे खिलाना है और राइटर प्राप्त करना है।

मुझे लगता है कि यह एक संस्करण से संबंधित समस्या हो सकती है और मेरा ghci संस्करण 7.4.1 है


2
अभ्यास के लिए, मैं घोषणा को आयात नहीं करने और इसे फ़ाइल में अपने दम पर लिखने की सलाह देता हूं।
sdcvvc

5
मैंने कुछ समय पहले लेखक से बात की थी, और उन्होंने पुष्टि की कि पुस्तक का ऑनलाइन संस्करण पुराना है। पीडीएफ पर एक और अधिक अप-टू-डेट संस्करण है: [यहाँ ]
इलेक्ट्रिक कॉफी

@ बिजली, मेरे पास एक ही सवाल है, क्या आप कृपया एक लिंक दे सकते हैं? आपका उपरोक्त लिंक टूट गया है
एम।

जवाबों:


126

पैकेज 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)

अब क्योंकि लेखक ReaderTmonad ट्रांसफॉर्मर के अंदर है , अगर आप आउटपुट लॉग करना चाहते हैं, तो आप इसका उपयोग नहीं कर सकते हैं 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फ़ंक्शन को सीधे रूपांतरित किए गए मोनड पर उपयोग कर सकते हैं , बिना मोनड ट्रांसफार्मर के माध्यम से स्पष्ट रूप से उठाने के साथ परेशान करने के लिए।


31
"मुझे लगता है कि यह तब अलग था जब LYAH लिखा गया था।" सही। यह mtlLYAH और RWH के लिखे जाने के कुछ ही समय बाद प्रमुख संस्करण 1. * से 2. * में बदल गया । अत्यधिक दुर्भाग्यपूर्ण समय जो शुरुआती लोगों के बीच बहुत भ्रम पैदा करता है और आगे बढ़ाता है।
डैनियल फिशर

2
मैं अभी जीएचसी संस्करण 7.8.3 का उपयोग कर रहा हूं और मुझे आयात करना था Control.Monad.Trans.Writer। इसके अलावा के प्रकार logNumberहै logNumber :: (Show a, Monad m) => a -> WriterT [[Char]] m aमेरे लिए।
किमीकैल

@kmikael शायद आपके पास mtlलाइब्रेरी स्थापित नहीं है (जिसका अर्थ है कि शायद आपके पास GHC का बेस इंस्टॉलेशन है, जैसे कि HGell प्लेटफॉर्म के बजाय minGHC की तरह)। एक कमांड प्रॉम्प्ट रन से cabal updateऔर cabal install mtlऔर फिर कोशिश करें।
क्रिस टेलर

क्रिस, मैं हास्केल प्लेटफ़ॉर्म का उपयोग कर रहा था और एमटीएल स्थापित किया गया था, मैंने इसे फिर से स्थापित किया और अब यह आपके उत्तर की तरह काम करने लगता है। मुझे नहीं पता कि क्या गलत था। धन्यवाद।
किमीकैल

पुस्तक की मुद्रित प्रति इसके बजाय सही है। इसमें एक पैरा शामिल है जिसमें बताया गया है कि बाद के writerस्थान पर इसका उपयोग किया जाता है Writer, मूल्य ctor, मॉड्यूल द्वारा निर्यात नहीं किया जाता है, जबकि पूर्व है, और इसका उपयोग उसी मूल्य को बनाने के लिए किया जा सकता है जिसे आप ctor के साथ बनाएंगे, लेकिन करता है पैटर्न मिलान की अनुमति न दें।
एनरिको मारिया डी एंजेलिस

8

"लेखक" नामक एक फ़ंक्शन एक "लेखक" निर्माता के बदले में उपलब्ध कराया जाता है। परिवर्तन:

logNumber x = Writer (x, ["Got number: " ++ show x])

सेवा:

logNumber x = writer (x, ["Got number: " ++ show x])


6
यह मौजूदा उत्तरों में क्या जोड़ता है?
dfeuer

1

मैं LYAH की कोशिश कर रहा से एक ऐसी ही संदेश मिला "कुछ monads अधिक के लिए" में ऑनलाइन हास्केल संपादक का उपयोग कर repl.it

मैंने आयात को इससे बदल दिया:

import Control.Monad.Writer

सेवा:

import qualified Control.Monad.Trans.Writer.Lazy as W

तो मेरा कोड अब इस तरह से काम करता है ( क्वांग के हास्केल ब्लॉग से प्रेरणा लेकर ):

import Data.Monoid
import qualified Control.Monad.Trans.Writer.Lazy as W


output :: String -> W.Writer [String] ()
output x = W.tell [x]


gcd' :: Int -> Int -> W.Writer [String] Int  
gcd' a b  
    | b == 0 = do  
        output ("Finished with " ++ show a)
        return a  
    | otherwise = do  
        output (show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b))
        gcd' b (a `mod` b)

main :: IO()
main = mapM_ putStrLn $ snd $ W.runWriter (gcd' 8 3) 

वर्तमान में यहाँ कोड चलने योग्य है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.