Is every repeatedly nested monads useful?

Viewed 145

By the title, I mean types like Monad m => m (m a).

When the structure of a monad is simple, I can easily think of a usage of such type:

  • [[a]], which is a multidimensional list

  • Maybe (Maybe a), which is a type adjoined by two error states

  • Either e (Either e a), which is like above, but with messages

  • Monoid m => (m,(m,a)), which is a writer monad with two things to write over

  • r -> r -> a, which is a reader monad with two things to read from

  • Identity (Identity a), which is still the identity monad

  • Complex (Complex a), which is a 2-by-2 matrix

But it goes haywire in my mind if I think about the following types:

  • ReadP (ReadP a)? Why would it be useful when ReadP isn't an instance of Read?

  • ReadPrec (ReadPrec a)? Like above?

  • Monad m => Kleisli m a (Kleisli m a b)?

  • IO (IO a)!? This must be useful. It just is too hard to think about it.

  • forall s. ST s (ST s a)!? This should be like the above.

Is there a practical use for such types? Especially for the IO one?

On the second thought, I might need to randomly pick an IO action. That's an example of IO (IO a) which focuses on inputs. What about one focusing on outputs?

2 Answers

In some sense, a monad can be thought of as a functor in which layers can be collapsed.

If the Monad class were defined more like the category-theory definition, it would look like

class Applicative m => Monad m where
    return ::  a -> m a
    join :: m (m a) -> m a

Using fmap with a function of type a -> m b results in a function of type m a -> m (m b). join is used to eliminate one layer of monad from the result. Since that's a common thing to do, one might define a function that does it.

foo :: Monad m => (a -> m b) -> m a -> m b
foo f ma = join (fmap f ma)

If you look carefully, you'll recognize foo as >>= with its arguments flipped.

foo = flip (>>=)

Since >>= is used more than join would be, the typeclass definition is

class Applicative m => Monad m where
    return :: a -> m a
    (>>=) :: m a -> (a -> m b) -> m b

and join is defined as a separate function

join :: Monad m => m (m a) -> m a
join mma = mma >>= id

Does not matter.

Monads are monads precisely because for every Monad ( Monad a ) we can always get Monad a. Such operation is called "join" and it's alternative operation to "bind" that could form definition of Monad. Haskell uses "bind" because its much more useful for composing monadic code :) (join can be implemented with bind, and bind with join - they are equivalent)

Does not matter

Is actually small lie, since ability to form Monad ( Monad a ) is also de facto part of what makes Monads monads. With Monad (Monad a) being transitional representation in some operations.

Full answer is: Yes, because that enables Monads. Though Monad ( Monad a ) can have extra "domain" meaning as you list for some of the Monads ;)

Related