Combining Monads (IEnumerable and Maybe as an Example)

I have a general question and a more specific question.

How to combine different monads in general? Does any combination of monad operators contain a light composition? Or do you need to write special methods to combine each possible pair of monads?

As a concrete example, I wrote a monastic monad. How can I use IEnumerable<IMaybe<T>> ? Besides manually searching the Maybe monad in LINQ extensions (for example, if(maybe.HasValue) ... inside select ), is there a "monadic" way to combine the two with their respective operations Monad and Monad?

Otherwise, if I need to write specific combination methods, is something like this the right way?

  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); } 
+7
source share
2 answers

Great question!

A monad transformer is a type that adds some functions to an arbitrary base monad, while preserving monad. Unfortunately, monad transformers are inexpressible in C # because they make significant use of higher-grade types. So, while working at Haskell,

 class MonadTrans (t :: (* -> *) -> (* -> *)) where lift :: Monad m => ma -> tma transform :: Monad m :- Monad (tm) 

Follow this line by line. The first line declares that the monad transformer is a type t that takes an argument of the form * -> * (i.e., a Type that expects one argument) and turns it into another type of the form * -> * . When you understand that all monads are of the form * -> * , you can see that the intention is that t turns the monads into other monads.

The next line says that all monad transformers must support the lift operation, which takes an arbitrary monad m and lifts it into the world of the transformer tm .

Finally, the transform method says that for any monad m , tm should also be a monad. I use the entry operator :- from the constraints package .


This will make more sense with an example. It uses a monad transformer, which adds Maybe -ness to the arbitrary base monad m . The nothing operator allows you to cancel the calculation.

 newtype MaybeT ma = MaybeT { runMaybeT :: m (Maybe a) } nothing :: Monad m => MaybeT ma nothing = MaybeT (return Nothing) 

For MaybeT to be a monad transformer, it must be a monad when its argument is a monad.

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

Now write an implementation of MonadTrans . The lift implementation wraps the return value of the base monad in Just . The transform implementation is uninteresting; he simply tells the GHC constraint resolver to make sure MaybeT really a monad whenever there is an argument.

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

Now we can write a monadic calculation that MaybeT uses to add failure, for example, to the State monad. lift allows you to use the standard methods State get and put , but we also have access to nothing if we need to fail the calculation. (I was thinking about using your IEnumerable (aka [] ) example, but there is something perverse about adding rejection to the monad that already supports it.)

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

What makes monad transformers really useful is their stackability. This allows you to create large monads with many possibilities from many small monads with one possibility each. For example, a given application may need to perform I / O, read configuration variables, and throw exceptions; it will be encoded with type type

 type Application = ExceptT AppError (ReaderT AppConfig IO) 

There are tools in the mtl package that will help you abstract from the exact collection and ordering of monad transformers in a given stack, allowing you to send calls to lift .

+1
source

In this particular case, you can implement IEnumerable<T> in MayBe<T> , so it either returns 0 or 1.

-one
source

All Articles