2012-02-09 16 views
6

Actualmente estoy escribiendo un proyecto en el que hago un uso intensivo del transformador de mónada ListT. Cuando se usan listas simples, implementar el no determinismo es muy fácil. Sin embargo, una vez que tuve que convertir mi código a ListT, se volvió mucho más complicado .¿Cómo convertir limpiamente entre listas y transformadores de mónada ListT?

Como un simple ejemplo: convertir [a]-ListT a requiere realmente componer dos funciones:

conv :: (Monad m) => [a] -> ListT m a 
conv = ListT . return 

Aunque es simple, me sorprende que no está ya allí.

Preguntas:

  • ¿Hay alguna manera mejor de manejar no determinismo donde se necesita un transformador mónada?
  • ¿Existen técnicas/bibliotecas para convertir limpiamente entre listas y ListT?

Las razones exactas son bastante complicados, así que realmente no quieren elaborar demasiado en eso.

Respuesta

6

No creo que haya ninguna biblioteca para esto; conv es una función increíblemente simple, después de todo, y al revés es solo runListT.

conv es similar a la liftMaybe frecuencia deseada cuando se utiliza MaybeT:

liftMaybe :: (Monad m) => Maybe a -> MaybeT m a 
liftMaybe = MaybeT . return 

recomendaría nombrarlo algo en la línea de liftList.

En cuanto a una mejor transformador de mónada no determinismo va, recomiendo tomar un vistazo al paquete logict, basado en LogicT transformador de Oleg, que es una mónada lógica de retroceso a base de continuidad con some helpful operations. Como extra, dado que [] es una instancia de MonadLogic, esas operaciones también funcionan en listas.


Curiosamente, podemos definir una función que generaliza el modelo de conv y liftMaybe:

import Data.Foldable (Foldable) 
import qualified Data.Foldable as F 

choose :: (Foldable t, MonadPlus m) => t a -> m a 
choose = F.foldr (\a b -> return a `mplus` b) mzero 

Esto probablemente hacer que el código bastante confuso, por lo que no recomendamos usarlo :)

+0

Sí, estoy de acuerdo en que 'conv' es una función simple. Estoy sorprendido de que todavía no esté allí. Apenas hay utilidades en el módulo 'ListT' que me hacen sentir como si reinventara la rueda. Eso es todo. – julkiewicz

0

Me encontré con esta pregunta unos meses más tarde porque me preguntaba algo similar a esto. Así que se me ocurrió lo siguiente:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-} 

import Control.Monad.Trans.Class 
import Control.Monad.Trans.Maybe 
import Control.Monad.Trans.List 


-- | Minimal implementation: either joinLift or joinT 
class (MonadTrans t, Monad m) => MonadTransJoin t m | m -> t, t -> m where 
    joinLift :: (Monad m', Monad (t m')) => m' (m a) -> t m' a 
    joinLift = joinT . lift 

    joinT :: (Monad m', Monad (t m')) => t m' (m a) -> t m' a 
    joinT = (>>= (joinLift . return)) 


instance MonadTransJoin MaybeT Maybe where 
    joinLift = MaybeT 
    joinT = (>>= maybe mzero return) 

instance MonadTransJoin ListT [] where 
    joinLift = ListT 
    joinT = (>>= foldr mcons mzero) 
     where mcons x xs = return x `mplus` xs 

Hasta ahora todo bien y mi método joinT para el par ListT/[] parece que tiene algo que ver con ehird de choose.

Pero el problema con esto es que en realidad no existe una interfaz uniforme entre un transformador de mónada y la mónada cuyo comportamiento dota a su mónada base. Tenemos MaybeT :: m (Maybe a) -> MaybeT m a y ListT :: m [a] -> ListT m a, pero OTOH tenemos StateT :: (s -> m (a, s)) -> StateT s m a. No sé si hay una forma de evitar esto, sino que requiere

Cuestiones relacionadas