2012-09-08 13 views
8

जब मैं LYAH के आखिरी अध्याय के माध्यम से चला गया और ListZipper के साथ मुलाकात की, मैं अपने आप को एक काम है कि यह एक राज्य इकाई ताकि स्रोत कोड की तरह और अधिक स्पष्ट दिखेगा बनाने के लिए दे दी है समय, मैं राइटर मोनड का लाभ उठाकर इस प्रक्रिया के लिए एक लॉग रखना चाहता था, लेकिन मुझे नहीं पता था कि इन दो मोनाड्स को एक साथ कैसे जोड़ा जाए।हैकेल में मैं राज्य और लेखक दोनों का लाभ कैसे उठा सकता हूं?</p> <pre><code>manipList = do goForward goForward goBack </code></pre> <p>और एक ही है:

मेरे समाधान राज्य के अंदर रखने के लिए एक [स्ट्रिंग] था, और मेरे स्रोत कोड

import Control.Monad 
import Control.Monad.State 

type ListZipper a = ([a], [a]) 

-- move focus forward, put previous root into breadcrumbs 
goForward :: ListZipper a -> ListZipper a 
goForward (x:xs, bs) = (xs, x:bs) 

-- move focus back, restore previous root from breadcrumbs 
goBack :: ListZipper a -> ListZipper a 
goBack (xs, b:bs) = (b:xs, bs) 

-- wrap goForward so it becomes a State 
goForwardM :: State (ListZipper a) [a] 
goForwardM = state stateTrans where 
    stateTrans z = (fst newZ, newZ) where 
     newZ = goForward z 

-- wrap goBack so it becomes a State 
goBackM :: State (ListZipper a) [a] 
goBackM = state stateTrans where 
    stateTrans z = (fst newZ, newZ) where 
     newZ = goBack z 

-- here I have tried to combine State with something like a Writer 
-- so that I kept an extra [String] and add logs to it manually 

-- nothing but write out current focus 
printLog :: Show a => State (ListZipper a, [String]) [a] 
printLog = state $ \(z, logs) -> (fst z, (z, ("print current focus: " ++ (show $ fst z)):logs)) 

-- wrap goForward and record this move 
goForwardLog :: Show a => State (ListZipper a, [String]) [a] 
goForwardLog = state stateTrans where 
    stateTrans (z, logs) = (fst newZ, (newZ, newLog:logs)) where 
     newZ = goForward z 
     newLog = "go forward, current focus: " ++ (show $ fst newZ) 

-- wrap goBack and record this move 
goBackLog :: Show a => State (ListZipper a, [String]) [a] 
goBackLog = state stateTrans where 
    stateTrans (z, logs) = (fst newZ, (newZ, newLog:logs)) where 
     newZ = goBack z 
     newLog = "go back, current focus: " ++ (show $ fst newZ) 

-- return 
listZipper :: [a] -> ListZipper a 
listZipper xs = (xs, []) 

-- return 
stateZipper :: [a] -> (ListZipper a, [String]) 
stateZipper xs = (listZipper xs, []) 

_performTestCase1 = do 
    goForwardM 
    goForwardM 
    goBackM 

performTestCase1 = 
    putStrLn $ show $ runState _performTestCase1 (listZipper [1..4]) 

_performTestCase2 = do 
    printLog 
    goForwardLog 
    goForwardLog 
    goBackLog 
    printLog 

performTestCase2 = do 
    let (result2, (zipper2, log2)) = runState _performTestCase2 $ stateZipper [1..4] 
    putStrLn $ "Result: " ++ (show result2) 
    putStrLn $ "Zipper: " ++ (show zipper2) 
    putStrLn "Logs are: " 
    mapM_ putStrLn (reverse log2) 

है लेकिन समस्या यह मुझे नहीं लगता कि कि यह एक अच्छा उपाय है के बाद से मैं अपने बनाए रखने के लिए है लॉग मैन्युअल रूप से। क्या राज्य मोनड और राइटर मोनड मिश्रण करने का कोई वैकल्पिक तरीका है ताकि वे एक साथ काम कर सकें?

उत्तर

16

आप monad transformers के लिए देख रहे हैं। मूल विचार WriterT जैसे प्रकार को परिभाषित करना है जो एक और मोनड लेता है और इसे Writer के साथ जोड़ता है जो एक नया प्रकार (जैसे WriterT log (State s)) बनाता है।

नोट: एक ऐसा सम्मेलन है जो ट्रांसफार्मर प्रकार पूंजी T के साथ समाप्त होता है। तो Maybe और Writer सामान्य मोनैड हैं और MaybeT और WriterT उनके ट्रांसफॉर्मर समकक्ष हैं।

मूल विचार बहुत सरल है: मोनैड के समूह के लिए, आप आसानी से बांधने पर अपने व्यवहार को जोड़कर कल्पना कर सकते हैं। सबसे सरल उदाहरण Maybe है। याद है कि सभी कि Maybe करता बाँध पर Nothing प्रचार है:

Nothing >>= f = Nothing 
Just x >>= f = f x 

तो यह इस व्यवहार के साथ किसी भी इकाई का विस्तार कल्पना करना आसान होना चाहिए। हम सब कुछ करते हैं Nothing पहले जांचें और फिर पुराने मोनाड के बांध का उपयोग करें। MaybeT प्रकार बिल्कुल यह करता है: यह मौजूदा मोनाड को लपेटता है और इस तरह की जांच के साथ प्रत्येक बाध्य करता है। आपको return को Just में आवश्यक रूप से मूल्य को लपेटकर और फिर आंतरिक मोनैड के return का उपयोग करके लागू करना होगा। सब कुछ काम करने के लिए थोड़ा और नलसाजी भी है, लेकिन यह एक महत्वपूर्ण विचार है।

आप Writer के लिए एक बहुत ही समान व्यवहार की कल्पना कर सकते हैं: पहले हम किसी भी नए आउटपुट को जोड़ते हैं तो हम पुराने मोनाड के बांध का उपयोग करते हैं। यह अनिवार्य रूप से WriterT का व्यवहार है। कुछ अन्य विवरण शामिल हैं, लेकिन बुनियादी विचार काफी सरल और उपयोगी है।

मोनाड ट्रांसफार्मर आपके जैसे मोनैड को "गठबंधन" करने का एक बहुत ही आम तरीका हैं। ट्रांसफॉर्मर के रूप में सबसे अधिक इस्तेमाल किए जाने वाले मोनैड के संस्करण हैं, IO के उल्लेखनीय अपवाद के साथ जो हमेशा आपके मोनैड स्टैक के आधार पर होना चाहिए। आपके मामले में, WriterT और StateT दोनों मौजूद हैं और आपके प्रोग्राम के लिए उपयोग किए जा सकते हैं।

+0

वही है जो मैं चाहता हूं!मैंने कोशिश की है और उनमें से दोनों (राइटर टी एंड स्टेटटी) अच्छी तरह से काम करते हैं, टाई! – Javran

12

टिखॉन जेल्विस मोनाड ट्रांसफार्मर के साथ एक अच्छा जवाब देता है। हालांकि, एक त्वरित समाधान भी है।

mtl में Control.Monad.RWS मॉड्यूल RWS इकाई है, जो Reader, Writer और State monads का एक संयोजन है निर्यात करता है।