tags:

views:

131

answers:

4

If I define the "bind" function like this:

(>>=) :: M a -> (a -> M' b) -> M' b

Will this definition help me if I want the result to be of a new Monad type, or I should use same Monad but with b in the same Monad box as before?

+6  A: 
KennyTM
Except Identity.
luqui
A: 

Not only will that definition not help, but it will seriously confuse future readers of your code, since it will break all expectations of use for it.

For instance, are both M and M' supposed to be Monads? If so, then how are they defined? Remember: the definition of >>= is part of the definition of Monad, and is used everywhere to define other Monad-using functions - every function besides return and fail themselves.

Also, do you get to choose which M and M' you use, or does the computer? If so, then how do you choose? Does it work for any two Monad instances, or is there some subset of Monad that you want - or does the choice of M determine the choice of M'?

It's possible to make a function like what you've written, but it surely is a lot more complicated than >>=, and it would be misleading, cruel, and potentially disastrous to try to cram your function into >>='s clothes.

BMeph
A: 

You may want to look at this sample from Oleg: http://okmij.org/ftp/Computation/monads.html#param-monad

Ed'ka
A: 

This can be a complicated thing to do, but it is doable in some contexts. Basically, if they are monads you can see inside (such as Maybe or a monad you've written) then you can define such an operation.

One thing which is sometimes quite handy (in GHC) is to replace the Monad class with one of your own. If you define return, >>=, fail you'll still be able to use do notation. Here's an example that may be like what you want:

class Compose s t where
  type Comp s t

class Monad m where
  return :: a -> m s a
  fail  :: String -> m a
  (>>=) :: (Compose s t) => m s a -> (a -> m t b) -> m (Comp s t) b
  (>>)  :: (Compose s t) => m s a -> m t b -> m (Comp s t) b
  m >> m' = m >>= \_ -> m'

You can then control which types can be sequenced using the bind operator based on which instances of Compose you define. Naturally you'll often want Comp s s = s, but you can also use this to define all sorts of crazy things.

For instance, perhaps you have some operations in your monad which absolutely cannot be followed by any other operations. Want to enforce that statically? Define an empty datatype data Terminal and provide no instances of Compose Terminal t.

This approach is not good for transposing from (say) Maybe to IO, but it can be used to carry along some type-level data about what you're doing.

If you really do want to change monads, you can modify the class definitions above into something like

class Compose m n where
  type Comp m n
  (>>=*) :: m a -> (a -> n b) -> (Compose m n) b

class Monad m where
  return :: a -> m a
  fail :: String -> m a
  (>>=) :: Compose m n => m a -> (a -> n b) -> (Compose m n) b
  m >>= f = m >>=* f
  (>>) :: Compose m n => m a -> (n b) -> (Compose m n) b
  m >> n = m >>=* \_ -> n

I've used the former style to useful ends, though I imagine that this latter idea may also be useful in certain contexts.

intoverflow