2015-09-14 16 views
8

में बदलना मैं उदाहरण लेने की कोशिश कर रहा हूं ExceptT a (StateT A M), कुछ ठोस प्रकार A और monad M के लिए, और उन्हें अपने नए कस्टम monads में लपेटें।मोनाड्स को टिडिंग करना - एक मोनैड ट्रांसफॉर्मर का नया टाइप मोनैड

सबसे पहले मैं पहचान है कि StateT A M अन्य संदर्भों में अक्सर दिखाई देता है और इस तरह मैंने तय कर लिया है कि यह एक इकाई M1 में है कि अकेले रैप करने के लिए और फिर M2 में ExceptT a M1 लपेट सबसे अच्छा होगा।

वांछित संपत्ति MonadState की M1 और M2 उदाहरणों और M के वर्ग बनाने के लिए है (मान यह MyMonadClass कहा जाता है की सुविधा देता है)। M2MonadError का एक उदाहरण होना चाहिए।

सबसे पहले मैं सरल प्रकार समानार्थी शब्दों द्वारा शुरू किया:

type MyState = StateT A M 
type MyBranch a = ExceptT a MyState 

तो मैंने सोचा था कि मैं पहले उदाहरण घोषणाओं (उदाहरण के लागू करने के बिना) और Thats जहाँ मैं पहली बार अटक गया स्केच होगा। instance MonadState A (MyState) सही वाक्यविन्यास नहीं लग रहा था। मैंने सोचा कि मुझे newtype MyState' a = StateT a M और फिर type MyState = MyState A बनाना होगा (भाषा एक्सटेंशन का उपयोग न करें जहां आवश्यक नहीं है)।

हालांकि एक बार मैंने समानार्थी को newtype घोषणाओं में परिवर्तित करना शुरू कर दिया, तो मैंने StateT A M और ExceptT ... प्रकारों से कनेक्शन खोना शुरू कर दिया।

newtype MyState' s a = MyState' { runMyState :: s -> (s, a) } 
type MyState = MyState' A 
newtype MyBranch e a = MyBranch { runMyBranch :: MyState (Either e a) } 

अब ट्रांसफार्मर कि पहले से ही गायब हो गया कार्यान्वित कर रहे हैं और मुझे लगता है कि मैं कुछ खास मतलब नहीं है करने के लिए कोशिश कर रहा हूँ। तो मेरा सवाल यह है कि: इस तरह के व्यवहार को नए यौगिक मोनैड्स में सही ढंग से कैसे लपेटें जो नीचे परतों को सुलभ बनाते हैं ताकि कोई अनावश्यक उठाने से बचा जा सके और चीजों को स्पष्ट और व्यवस्थित रख सके।

उत्तर

9

सामान्य पैटर्न आपके पूर्ण ट्रांसफार्मर ढेर के लिए एक नया प्रकार परिभाषित करना है।

data A = A 
data E = E 

newtype MyBranchT m a = MyBranchT { getMyBranchT :: ExceptT E (StateT A m) a } 

अगर वहाँ है कि अपने दम पर सार्थक नई क्षमताओं को जोड़ने के ढेर में से किसी टुकड़े आप उन लोगों को भी टुकड़े के लिए newtypes परिभाषित चाहते हैं।

फिर आप विभिन्न मोनैड कक्षाओं के उदाहरण प्राप्त करने के लिए GeneralizedNewtypeDeriving का उपयोग करते हैं।

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 

-- base 
import Control.Applicative 
import Control.Monad 

-- transformers 
import Control.Monad.IO.Class 
import Control.Monad.Trans.Class 
import Control.Monad.Trans.Except 
import Control.Monad.Trans.State.Lazy 

-- mtl classes 
import Control.Monad.Cont.Class 
import Control.Monad.Error.Class 
import Control.Monad.Reader.Class 
import Control.Monad.State.Class 
import Control.Monad.Writer.Class 

data A = A 
data E = E 

newtype MyBranchT m a = MyBranchT { getMyBranchT :: ExceptT E (StateT A m) a } 
    deriving (Functor, Applicative, Monad, MonadIO,  -- classes from base and transformers 
       {- Alternative, MonadPlus, -}    -- if E is a monoid 
       MonadState A, MonadError E,    -- classes from mtl that MyBranchT provides 
       MonadCont, MonadReader r, MonadWriter w) -- classes from mtl that might be available from m 

आपको हाथ से MonadTrans उदाहरण लिखना होगा। lift स्टैक में प्रत्येक ट्रांसफार्मर के लिए हमेशा lift हमेशा होता है।

instance MonadTrans MyBranchT where 
    lift = MyBranchT . lift . lift 

अगर वहाँ है कि अपने दम पर सार्थक नई क्षमताओं को जोड़ने X तुम भी, जहां उनकी क्षमताओं के लिए एक नया MonadX वर्ग को परिभाषित होता इकाई transfomers (StateT से प्रत्येक के लिए MonadX उदाहरणों बारे में ढेर के किसी भी टुकड़े कर रहे हैं, ExceptT , ContT, आदि) यदि संभव हो, और अपने ट्रांसफार्मर स्टैक (MyBranchT) के लिए MonadX उदाहरण प्राप्त करें।

तुम भी आमतौर पर runMyBranchT करने के लिए MyBranchT Identity के लिए एक प्रकार का पर्याय और कार्यों बनाती हूँ और runMyBranch

import Data.Functor.Identity 

type MyBranch a = MyBranchT Identity a 

runMyBranchT :: MyBranchT m a -> A -> m (Either E a, A) 
runMyBranchT mb s = runStateT (runExceptT (getMyBranchT mb)) s 

runMyBranch :: MyBranch a -> A -> (Either E a, A) 
runMyBranch mb s = runIdentity $ runMyBranchT mb s 
+0

आपको बहुत बहुत धन्यवाद, यह एक महान जवाब है। क्या मैं किसी भी तरह से भाषा विस्तार के बिना कर सकता हूं जैसे कि 'सामान्यीकृत न्यूटाइपडिविंग'? – jakubdaniel

+0

@JakubDaniel निश्चित रूप से, आप हाथ से सभी उदाहरण लिख सकते हैं। लेकिन आप नहीं चाहते (और शायद गड़बड़ कर देंगे), इस प्रकार 'सामान्यीकृत न्यूटाइपडिविंग'। – Cirdec

+0

जीएचसीआई में ': set -ddump-deriv'' आदेश चलाने वाले वास्तविक कोड को देखने के लिए, यह आपको सभी कोड जीएचसी उदारता से आपके लिए लिखता है। 'डेटा एबी = ए | बी ईक 'डंप' उदाहरण GHC.Classes.Eq Ghci5.AB जहां (GHC.Classes। ==) Ghci5.A Ghci5.A = GHC.Types.True; (जीएचसी.क्लास। ==) Ghci5.B Ghci5.B = जीएचसी.Types.True; (जीएचसी.क्लास। ==) _ _ = जीएचसी। टाइप्स। फाल्स ' –

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