2012-05-24 7 views
5

मेरे पास ErrorT सहित एक मोनड ट्रांसफार्मर स्टैक है और मैं पूरी चीज के आसपास ContT r ट्रांसफॉर्मर लपेटना चाहता हूं। जब मैं ऐसा करने का प्रयास करता हूं, तो throwError पर मेरी कॉल प्रकार त्रुटियां उत्पन्न करती है - जाहिर है ContT r स्वचालित रूप से MonadError का उदाहरण नहीं है। ठीक है, मैंने सोचा था कि - मैं सिर्फ एक में बना देंगे: liftCatch के कुछ उपयुक्त परिभाषा का उपयोगकंटेंट को MonadError का उदाहरण क्यों नहीं बनाया जा सकता है?

instance MonadError e m => MonadError e (ContT r m) where 
    throwError = lift . throwError 
    catchError = liftCatch . catchError 

। मैं UndecidableInstances pragma (मैं धारणा है कि यह बहुत चिंताजनक नहीं है के तहत हूं, जैसे देखने this question) का उपयोग करने के लिए खुश हूँ

src\Language\Types.hs:68:10: 
    Illegal instance declaration for `MonadError e (ContT r m)' 
     (the Coverage Condition fails for one of the functional dependencies; 
     Use -XUndecidableInstances to permit this) 
    In the instance declaration for `MonadError e (ContT r m)' 

लेकिन अगर वहाँ बनाने में कठिनाई था मैंने सोचा: लेकिन अब मैं जब संकलन त्रुटियों मिल MonadError के उदाहरण में निरंतरता ट्रांसफार्मर - मुझे लगता है कि अगर यह ठीक था, तो Control.Monad.Trans पैकेज के लेखकों ने इसे पहले से ही किया होगा ... सही?

+1

यह ठीक है, लेकिन करता है ट्रांसफॉर्मर्स लाइब्रेरी के लेखकों के लिए बेहद खतरनाक और गैर-पोर्टेबल है जो अनिश्चित वित्तीय लेते हैं। –

उत्तर

8

ContT और ErrorT दोनों गैर-मानक नियंत्रण प्रवाह की अनुमति देते हैं। MTL में ContT आसपास ErrorT प्रकार रैप करने के लिए एक तरीका होता है:

instance (Error e, MonadCont m) => MonadCont (ErrorT e m) 

लेकिन इन दो इकाई ट्रांसफार्मर निकल नहीं है। याद:

newtype Identity a = Identity {runIdentity :: a} 
newtype ErrorT e m a = ErrorT {runErrorT :: m (Either e a)} 
newtype ContT r m a = ContT {runContT :: (a -> m r) -> m r} 

ErrorT String (ContT Bool Identity)() जो पैकेज MTL में ठीक है हो सकता है:

ErrorT (ContT (\ (k :: Either String() -> Identity Bool) -> k (Right()))) 

ContT r (ErrorT e Identity) a पैकेज MTL में ठीक नहीं है। लेकिन आप इसे लिख सकते हैं।

संयुक्त मोनड में आप (>> =) के अर्थशास्त्र क्या हैं? आप नेस्टेड त्रुटि हैंडलर के अपने ढेर को गैर-कॉलल कॉलसीसी के साथ संवाद करने की अपेक्षा कैसे करते हैं?

यहाँ कैसे मैं इसे लिख सकते है:

{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-} 
import Control.Monad 
import Control.Monad.Cont 
import Control.Monad.Error 
import Data.Function 
import Data.IORef 

handleError :: MonadError e m => (e -> m a) -> m a -> m a 
handleError = flip catchError 

test2 :: ErrorT String (ContT() IO)() 
test2 = handleError (\e -> throwError (e ++ ":top")) $ do 
    x <- liftIO $ newIORef 1 
    label <- callCC (return . fix) 
    v <- liftIO (readIORef x) 
    liftIO (print v) 
    handleError (\e -> throwError (e ++ ":middle")) $ do 
    when (v==4) $ do 
     throwError "ouch" 
    when (v < 10) $ do 
     liftIO (writeIORef x (succ v)) 
     handleError (\e -> throwError (e ++ ":" ++ show v)) label 
    liftIO $ print "done" 

go2 = runContT (runErrorT test2) (either error return) 

{- 

*Main> go2 
1 
2 
3 
4 
*** Exception: ouch:middle:top 

-} 

तो बस MTL साथ ऊपर काम करता है, यहाँ नया उदाहरण है और यह कैसे काम करता है:

instance MonadError e m => MonadError e (ContT r m) where 
    throwError = lift . throwError 
    catchError op h = ContT $ \k -> catchError (runContT op k) (\e -> runContT (h e) k) 

test3 :: ContT() (ErrorT String IO)() 
test3 = handleError (\e -> throwError (e ++ ":top")) $ do 
    x <- liftIO $ newIORef 1 
    label <- callCC (return . fix) 
    v <- liftIO (readIORef x) 
    liftIO (print v) 
    handleError (\e -> throwError (e ++ ":middle")) $ do 
    when (v==4) $ do 
     throwError "ouch" 
    when (v < 10) $ do 
     liftIO (writeIORef x (succ v)) 
     handleError (\e -> throwError (e ++ ":" ++ show v)) label 
    liftIO $ print "done" 

go3 = runErrorT (runContT test3 return) 

{- 

*Main> go3 
1 
2 
3 
4 
Left "ouch:middle:3:middle:2:middle:1:middle:top" 

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