2011-11-10 16 views
7

Tengo una pregunta general y otra más específica.Combinación de mónadas (IEnumerable y Maybe como ejemplo)

¿Cómo se combinan diferentes mónadas en general? ¿Alguna combinación de los operadores de mónada permite una fácil composición? ¿O uno tiene que escribir métodos ad-hoc para combinar cada posible par de mónadas?

Como ejemplo específico, escribí una Mónada Maybe. ¿Cómo podría uno usar un IEnumerable<IMaybe<T>>? Además de profundizar manualmente en la mónada Maybe dentro de las extensiones LINQ (como: if(maybe.HasValue) ... dentro de las cláusulas select), ¿existe alguna forma "monádica" de combinar las dos con sus respectivas operaciones de mónada Bind, etc.?

De lo contrario, si tengo que escribir métodos de combinación específicos, ¿es algo así como la forma correcta de hacerlo?

public static IEnumerable<B> SelectMany<A, B>(this IEnumerable<A> sequence, Func<A, IMaybe<B>> func) 
    { 
     return from item in sequence 
       let result = func(item) 
       where result.HasValue 
       select result.Value; 
    } 


    public static IEnumerable<C> SelectMany<A, B, C>(this IEnumerable<A> sequence, Func<A, IMaybe<B>> func, Func<A, B, C> selector) 
    { 
     return from item in sequence 
       let value = item 
       let maybe = func(item) 
       where maybe.HasValue 
       select selector(value, maybe.Value); 
    } 
+0

Encontré una referencia muy interesante de la combinación de mónadas: http://heinrichapfelmus.github.com/operational/Documentation.html#alternatives-to-monad-transformers Defiende una forma general de combinar mónadas. También debería leer este documento, que describe cómo combinar tipos de datos de forma general y extensible: http://www.cs.ru.nl/~wouters/Publications/DataTypesALaCarte.pdf –

Respuesta

-1

En este caso específico, se podría aplicar en IEnumerable<T>MayBe<T>, por lo que o bien devuelve el valor 0 o 1.

1

¡Gran pregunta!

Un transformador de mónada es un tipo que agrega cierta funcionalidad a una base de mónada arbitraria, al tiempo que preserva la mónada. Lamentablemente, los transformadores de mónada son inexpresables en C# porque hacen un uso esencial de los tipos de mayor nivel. Por lo tanto, trabajando en Haskell,

class MonadTrans (t :: (* -> *) -> (* -> *)) where 
    lift :: Monad m => m a -> t m a 
    transform :: Monad m :- Monad (t m) 

Repasemos esta línea por línea. La primera línea declara que un transformador de mónada es un tipo t, que toma un argumento del tipo * -> * (es decir, un tipo que espera un argumento) y lo convierte en otro tipo de tipo * -> *. Cuando te das cuenta de que todas las mónadas tienen el tipo * -> *, puedes ver que la intención es que t convierta las mónadas en otras mónadas.

La siguiente línea dice que todos los transformadores monad deben apoyar una operación lift, que tiene una mónada arbitraria m y ascensores en el mundo del transformador t m.

Finalmente, el método transform dice que para cualquier mónada m, t m también debe ser una mónada. Estoy utilizando el vinculación operador :- de the constraints package.


Esto tendrá más sentido con un ejemplo. Aquí hay un transformador de mónada que agrega Maybe -ness a una base de mónada arbitraria m. El operador nothing nos permite abortar el cálculo.

newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) } 

nothing :: Monad m => MaybeT m a 
nothing = MaybeT (return Nothing) 

Para que MaybeT a ser un transformador mónada, tiene que ser una mónada siempre que su argumento es una mónada.

instance Monad m => Monad (MaybeT m) where 
    return = MaybeT . return . Just 
    MaybeT m >>= f = MaybeT $ m >>= maybe (return Nothing) (runMaybeT . f) 

Ahora para escribir la implementación MonadTrans. La implementación de lift envuelve el valor de retorno de la mónada base en Just.La implementación de transform no es interesante; simplemente le dice al solucionador de restricciones de GHC que verifique que MaybeT es de hecho una mónada siempre que su argumento sea.

instance MonadTrans MaybeT where 
    lift = MaybeT . fmap Just 
    transform = Sub Dict 

Ahora podemos escribir un cálculo que utiliza monádico MaybeT añadir la falta de, por ejemplo, la mónada State. lift nos permite utilizar los métodos State estándar get y put, pero también tenemos acceso a nothing si necesitamos fallar el cálculo. (Pensé en usar el ejemplo de IEnumerable (también conocido como []), pero hay algo de perverso en la adición de la falta de una mónada que ya lo soporta.)

example :: MaybeT (State Int)() 
example = do 
    x <- lift get 
    if x < 0 
    then nothing 
    else lift $ put (x - 1) 

Lo que hace que los transformadores monad realmente útil es su capacidad de apilamiento. Esto le permite componer grandes mónadas con muchas capacidades de muchas pequeñas mónadas con una capacidad cada una. Por ejemplo, una aplicación determinada puede necesitar IO, leer variables de configuración y lanzar excepciones; esto se codifica con un tipo como

type Application = ExceptT AppError (ReaderT AppConfig IO) 

Existen herramientas en the mtl package que ayudan a abstraer sobre la recogida y el orden de los transformadores monad precisa en una pila dada, lo que le permite eludir las llamadas a lift.

Cuestiones relacionadas