2012-05-24 22 views
5

Tengo una pila de transformador de mónada que incluye un ErrorT y quiero envolver un transformador ContT r alrededor de todo. Cuando trato de hacer eso, mis llamadas al throwError generan errores tipo - aparentemente ContT r no es automáticamente una instancia de MonadError. Bien, pensé - Voy a convertirlo en uno:¿Por qué no se puede convertir ContT una instancia de MonadError?

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

utilizando una definición adecuada de liftCatch. Pero ahora tengo errores al compilar:

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)' 

estoy feliz de usar el pragma UndecidableInstances (estoy bajo la impresión de que no es demasiado preocupante, por ejemplo, véase this question) pero me preguntaba si había una dificultad en la toma de el transformador de continuación en una instancia de MonadError - Supongo que si estuviera bien, los autores del paquete Control.Monad.Trans lo habrían hecho ya ... ¿no?

+1

Está bien, pero hace tomar IndecisasInstancias que son demasiado peligrosas y no portátiles para los autores de la biblioteca de transformadores. –

Respuesta

8

ContT y ErrorT ambos permiten un flujo de control no estándar. Hay una manera de envolver el tipo ErrorT alrededor ContT en MTL:

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

Pero estos dos transformadores mónada no conmutan. Recordando:

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)() que está bien en mtl paquete podría ser:

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

ContT r (ErrorT e Identity) a no está bien en mtl paquete. Pero puedes escribirlo.

¿Cuál es la semántica de (>> =) que desea en la mónada combinada? ¿Cómo espera que su pila de manejadores de errores anidados interactúe con callCC no local?

Aquí es cómo podría escribirlo:

{-# 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 

-} 

Así que los trabajos anteriores con sólo el MTL, aquí es la nueva instancia y cómo funciona:

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" 

-} 
Cuestiones relacionadas