Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is 'Chaining operations' the "only" thing that the Monad class solves?

To clarify the question: it is about the merits of the monad type class (as opposed to just its instances without the unifying class).

After having read many references (see below), I came to the conclusion that, actually, the monad class is there to solve only one, but big and crucial, problem: the 'chaining' of functions on types with context. Hence, the famous sentence "monads are programmable semicolons". In fact, a monad can be viewed as an array of functions with helper operations.

I insist on the difference between the monad class, understood as a general interface for other types; and these other types instantiating the class (thus, "monadic types").

I understand that the monad class by itself, only solves the chaining of operators because mainly, it only mandates its type instances to have bind >>= and return, and tell us how they must behave. And as a bonus, the compiler greatyly helps the coding providing do notation for monadic types.

On the other hand, it is each individual type instantiating the monad class which solves each concrete problem, but not merely for being a instance of Monad. For instance Maybe solves "how a function returns a value or an error", State solves "how to have functions with global state", IO solves "how to interact with the outside world", and so on. All theses classes encapsulate a value within a context.

But soon or later, we will need to chain operations on such context-types. I.e., we will need to organize calls to functions on these types in a particular sequence (for an example of such a problem, please read the example about multivalued functions in You could have invented monads).

And you get solved the problem of chaining, if you have each type be an instance of the monad class. For the chaining to work you need >>= just with the exact signature it has, no other. (See this question).

Therefore, I guess that the next time you define a context data type T for solving something, if you need to sequence calls of functions (on values of T) consider making T an instance of Monad (if you need "chaining with choice" and if you can benefit from the do notation). And to make sure you are doing it right, check that T satisfies the monad laws

Then, I ask two questions to the Haskell experts:

  1. A concrete question: is there any other problem that the monad class solves by ifself (leaving apart monadic classes)? If any, then, how it compares in relevance to the problem of chaining operations?
  2. An optional general question: are my conclusions right, am I misunderstanding something?

References

Tutorials

  • Monads in pictures Definitely worth it; read this one first.
  • Fistful of monads
  • You could have invented monads
  • Monads are trees (pdf)

StackOverflow Questions & Answers

  • How to detect a monad
  • On the signature of >>= monad operator
like image 656
cibercitizen1 Avatar asked Jan 21 '14 14:01

cibercitizen1


People also ask

What is the purpose of a monad?

A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.

Is monad a design pattern?

In functional programming, a monad is a software design pattern with a structure that combines program fragments (functions) and wraps their return values in a type with additional computation.

What is monad in oops?

In terms of OO programming, a monad is an interface (or more likely a mixin), parameterized by a type, with two methods, return and bind that describe: How to inject a value to get a monadic value of that injected value type; How to use a function that makes a monadic value from a non-monadic one, on a monadic value.

Is promise a monad?

If you want to see how sloppy your thinking is, try writing. If you want to see how sloppy your writing is, try writing math. “Monad” is a well-defined mathematical object that must satisfy axioms known as the monad laws: Left Identity, Right Identity, and Associativity.


2 Answers

You're definitely on to something in the way that you're stating this—there are many things that Monad means and you've separated them out well.

That said, I would definitely say that chaining operations is not the primary thing solved by Monads. That can be solved using plain Functors (with some trouble) or easily with Applicatives. You need to use the full monad spec when "chaining with choice". In particular, the tension between Applicative and Monad comes from Applicative needing to know the entire structure of the side-effecting computation statically. Monad can change that structure at runtime and thus sacrifices some analyzability for power.


To make the point more clear, the only place you deal with a Monad but not any specific monad is if you're defining something with polymorphism constrained to be a Monad. This shows up repeatedly in the Control.Monad module, so we can examine some examples from there.

sequence     :: [m a] -> m [a] forever      :: m a   -> m b foldM        :: (a -> b -> m a) -> a -> [b] -> m a 

Immediately, we can throw out sequence as being particular to Monad since there's a corresponding function in Data.Traversable, sequenceA which has a type slightly more general than Applicative f => [f a] -> f [a]. This ought to be a clear indicator that Monad isn't the only way to sequence things.

Similarly, we can define foreverA as follows

foreverA :: Applicative f => f a -> f b foreverA f = flip const <$> f <*> foreverA f 

So more ways to sequence non-Monad types. But we run into trouble with foldM

foldM             :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a foldM _ a []      =  return a foldM f a (x:xs)  =  f a x >>= \fax -> foldM f fax xs 

If we try to translate this definition to Applicative style we might write

foldA             :: (Applicative f) => (a -> b -> f a) -> a -> [b] -> f a foldA _ a []      =  pure a foldA f a (x:xs)  = foldA f <$> f a x <*> xs 

But Haskell will rightfully complain that this doesn't typecheck--each recursive call to foldA tries to put another "layer" of f on the result. With Monad we could join those layers down, but Applicative is too weak.


So how does this translate to Applicatives restricting us from runtime choices? Well, that's exactly what we express with foldM, a monadic computation (a -> b -> m a) which depends upon its a argument, a result from a prior monadic computation. That kind of thing simply doesn't have any meaning in the more purely sequential world of Applicative.

like image 119
J. Abrahamson Avatar answered Sep 20 '22 06:09

J. Abrahamson


To solve the problem of chaining operations on an individual monadic type, it's not at all necessary to make it an instance of Monad and be sure the monad laws are satisfied. You could just implement a chaining operation directly on your type.

It would probably be very similar to the monadic bind, but not necessarily exactly the same (recall that bind for lists is concatMap, a function that exists anyway, but with the arguments in a different order). And you wouldn't have to worry about the monad laws, because you would have a slightly different interface for each type, so they wouldn't have any common requirements.

To ask what problem the Monad type class itself solves, look at all the functions (in Control.Monad and else where) that work on values in any monadic type. The problem solved is code reuse! Monad is exactly the part of all the monadic types that is common to each and every one of them. That part is sufficient on its own to write useful computations. All of these functions could be implemented for any individual monadic type (often more directly), but they've already been implemented for all monadic types, even the ones that don't exist yet.

You don't write a Monad instance so that you can chain operations on your type (often you already have a way of chaining, in fact). You write a Monad instance for all the code that automatically comes along with the Monad instance. Monad isn't about solving any problem for any single type, it's about a way of viewing many disparate types as instances of a single unifying concept.

like image 36
Ben Avatar answered Sep 19 '22 06:09

Ben