2011-07-06 20 views
5

El problema es este. Tengo:mapMonadTrans :: MonadTrans xT => (m a -> n b) -> xT m a -> xT n b

f :: MonadIO m => ReaderT FooBar m Answer; 
f = (liftIO getArgs) >>= ... 

Necesito ejecutar esto con argumentos modificados. Sin embargo, dado que m es desconocida, no puedo simplemente uso

mapReaderT (withArgs args) :: ReaderT r IO b -> ReaderT r IO b 

ya que necesito alguna manera de transformar (withArgs args) en m para todo m.

Una posibilidad es que encontré para definir mis propias withArgs, por lo tanto:

import System.Environment (setArgs, freeArgv); 
withArgv new_args act = do { 
    pName <- liftIO System.Environment.getProgName; 
    existing_args <- liftIO System.Environment.getArgs; 
    bracket (liftIO $ setArgs new_args) 
      (\argv -> do { 
         _ <- liftIO $ setArgs (pName:existing_args); 
         liftIO $ freeArgv argv; 
        }) 
      (const act); 
}; 

withArgs xs act = do { 
    p <- liftIO System.Environment.getProgName; 
    withArgv (p:xs) act; 
}; 

Sin embargo, esto es una chapuza, y específica para una función - que tendría que volver a escribir todos los withX :: X -> IO a -> IO a, por ejemplo, Control.Exception.handle

¿Cuál es, en su caso, una mejor manera de hacerlo?

Editar: En el caso del identificador, encontré Control.Monad.CatchIO. En el otro caso, utilicé otro, más breve kludge (no vale la pena publicarlo) para evitar el kludge anterior. ¡Todavía buscando una mejor solución!

+0

¿Qué sucede si elimina la firma de tipo en 'f'? Me pregunto si restringir a 'MonadIO' es demasiado restrictivo. –

+0

Necesito hacer E/S en 'f'. De lo contrario, eso sería grandioso. (En realidad, tengo un tipo de datos a con una función para producir algún valor de tipo b, y la función debe ser lo suficientemente general para que algunos valores de tipo a puedan hacer E/S para producir b.) –

+0

@strake : tenga en cuenta que hay un problema con Control.Monad.CatchIO. A saber, si está utilizando un transformador de mónada de cortocircuito (por ejemplo, ErrorT), es posible que no se comporte como esperaba. Si esto es un defecto de diseño o mal uso está abierto a la interpretación, pero debe tenerlo en cuenta. Ver http://andersk.mit.edu/haskell/monad-peel/ para más detalles. –

Respuesta

4

El paquete monad-control hará esto. Creo que quieres la función liftIOOp_ de Control.Monad.IO.Control.

Específicamente,

liftIOOp_ (withArgs newArgs) f 

debe hacer lo que quiera. También puede levantar cosas como bracket, con la función liftIOOp.

+0

Ace! Justo lo que necesito. ¡Gracias! –

4

Creo que el interleavableIO package soluciona este problema. Se discute en this cafe thread.

+0

Podría, pero, desafortunadamente, es muy desconcertante. ¿Cómo se usa? –

+0

@strake, Desafortunadamente, nunca lo he usado, solo recordé que se discutió. Tal vez otra pregunta SO está en orden? – luqui

8

Parte de lo que está buscando es un levantamiento de un homomorfismo de mónada en un transformador de mónada.

class MonadHoist t where 
    hoist :: (Monad m, Monad n) => (forall a. m a -> n a) -> t m a -> t n a 

    t :: Monad m => t Identity a -> t m a 
    t = hoist (return . runIdentity) 

Es decir, dado un homomorfismo mónada fm-n, se puede obtener un homomorfismo mónada de t m a t n usando elevador.

Un homomorfismo de mónada es un poco más fuerte que los tipos mencionados anteriormente, es decir, es responsable de preservar las leyes de mónadas.

f . return = return 
f . fmap g = fmap g . f 
f . join = join . f . fmap f 
     = join . fmap f . f -- by the second law 
     = (>>= f) . f  -- >>= in terms of join 

Aviso el cuantificador que se coló en el tipo de hoist, MonadHoist resulta necesario que la flexibilidad para casi todos los casos! (Reader pasa a ser el único caso en el que no lo hace. Intente escribir MaybeT sin él.)

Los transformadores de Monad pueden, en general, crear instancias de esta clase. Por ejemplo:

instance MonadHoist (StateT s) where 
    hoist f (StateT m) = StateT (f . m) 

instance MonadHoist (ReaderT e) where 
    hoist f (ReaderT m) = ReaderT (f . m) 

instance MonadHoist MaybeT where 
    hoist f (MaybeT m) = MaybeT (f m) 

estos momentos, no ofrecemos en transformers o mtl paquete porque requeriría una Rank2Type, pero es bastante sencillo de implementar.

Si hay suficiente demanda para él, felizmente lo empacaré en un paquete monad-extras.

Ahora, dije parte, porque mientras esto responde la pregunta dada por el tipo en el tema de su publicación, ¡no aborda la necesidad reflejada por la mayor parte del texto asociado con su pregunta!

Para eso, es probable que desee seguir el consejo de luqui. =)

+0

Buena idea. En realidad, (como acabo de notar), alguien más se dio cuenta: http://hackage.haskell.org/package/mmtl –

0

parece que se puede utilizar runReaderT para obtener el efecto deseado, así:

*> :t withArgs [] (runReaderT f FooBar) 
withArgs [] (runReaderT f FooBar) :: IO Answer 

donde FooBar es un poco de constructor de datos y f se define como anteriormente.

Cuestiones relacionadas