1

My understanding is that for the do monad, each step has a continuation and a closure.

This author writes:

We've seen that purity, strong types, and monads can:

...

  • Prevent bugs that might arise from confusion between different phases of execution.

My question is: What is the category of bugs that monads prevent?

Community
  • 1
  • 1
hawkeye
  • 34,745
  • 30
  • 150
  • 304
  • That has more to do with the purity and strong types part than the monad part. Also, do isn't a monad, it's just a shorthand for calling the `Monad` methods. – David Young Mar 06 '15 at 16:31
  • Thanks - that's helpful - could you expand that into an answer? – hawkeye Mar 06 '15 at 21:35
  • Well, monads are a bit notoriously tricky to explain without spreading misconceptions. Have you seen this article yet? http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html. Also, I wrote an answer that I feel explains a specific kind of monad decently well and that may help (it was written in response to a question from someone trying to avoid monads): http://stackoverflow.com/questions/23188645/non-monadic-error-handling-in-haskell/23207315. Unfortunately these don't really go into the bug prevention aspect of strong typing, I'll have to think about other resources for that. – David Young Mar 09 '15 at 20:02
  • One other piece of advice I can give is definitely the best way to learn about `Monad` is to look at *specific* monads. Once you look at and use them enough of them for long enough, you start to get a mental picture of how this generalizes to the more general idea of a "monad." Try to make sure that you don't think that just because one instance of `Monad` works in a certain way, all of them must work in that certain way though. – David Young Mar 09 '15 at 20:03

2 Answers2

0

Let's say you've written an algorithm that must receive a callback. You don't know what the callback will want to do, or what it's capable of. The most generic way to accept such a callback is to receive a function like:

Monad m => a -> m b

This gives complete freedom to your caller (who can chose any m that is a Monad), while denying any such freedom to your library. This prevents the introduction of side-effects in an otherwise pure library, while allowing side-effects to occur if the caller desires them.

I've used this pattern before in a pure register allocator. In that library I never needed effects myself, but wanted to allow the user to make use of their own effects (such as State) for creating new blocks and move instructions.

John Wiegley
  • 6,972
  • 1
  • 16
  • 20
0

Effect separation

Like ordinary types give you a way to distinguish data, monads give you a way to distinguish effects.

Elixir

Here is an example in Elixir, which is a nearly-pure functional language on top of Erlang. This example is derived from a real-world situation that occurs very often at my work.

def handle_call(:get_config_foo, _, state) do:
  {:reply, state.foo, state}
end

def handle_call(:get_bar, _, state) do:
  {:reply, state.bar, state}
end

def handle_call({:set_bar, bar}, _, state) do:
  {:reply, :ok, %{state | bar: bar}}
end

This defines an API of a GenServer, which is a little Erlang node that holds some state and allows you to query it as well as change it. The first call, :get_config_foo reads an immutable config setting. The second set of calls, :get_bar and {:set_bar, bar}, gets and sets a mutable state variable.

How I wish I had monads here, to prevent the following bug:

def handle_call({:set_bar, bar}, _, state) do:
  {:reply, :ok, %{state | foo: bar}}
end

Can you spot the mistake? Well, I just wrote a readonly value. Nothing in Elixir prevents this. You can't mark some parts of your GenServer state as readonly, and others als read-write.

Haskell: Reader and State

In Haskell you can use different monads to specify different kinds of effects. Here are readonly state (Reader) and read-write state:

data Reader r a = Reader (r -> a)
data State s a = State (s -> (a, s))

Reader allows you to access the config state r to return some value a. State allows you to read the state, return some value and to modify the state. Both are monads, which essentially means that you can chain those state accesses sequentially in an imperative fashion. In Reader you can first read one config setting, and then (based on that first setting), read another setting. In State, you can read the state, and then (based on what you read) modify it in a further way. But you can never modify the state in Reader while you execute it.

Deterministic effects

Let me repeat this. Binding several calls to Reader assures you that you can never modify the reader state inbetween. If you have getConfigFoo1 :: Reader Config Foo1 and getConfigFoo2 :: Foo1 -> Reader Config Foo2 and you do getAllConfig = getConfigFoo1 >>= getConfigFoo2, then you have certainty that both queries will run on the same Config. Elixir does not have this feature and allows the above mentioned bug go unnoticed.

Other effects where this is useful is Writer (write-only state, e.g. logging) and Either (exception handling). When you have Writer instead of Reader or State, you can be sure that your state is ever only appended to. When you have Either, you know exactly the type of exceptions that can occur. This is all much better than using IO for logging and exception handling.

Turion
  • 5,684
  • 4
  • 26
  • 42