monad-embed FAQ

Q: Why not use applicative functors?

Applicative functors are less powerful than monads. monad-embed offers the full power of monads with a syntax as nice, if not nicer, than that of applicative functors.

This allows all sorts of neat things like using the IO monad to filter a list by asking the user about each item as we get to it, or using the StateT monad transformer to count the number of comparisons needed to quicksort a list.

Note that monad-embed and applicative functors are not mutually exclusive. If you want an applicative functor that isn't a monad, you can use pure and <*> just like you would in Haskell. monad-embed could even be combined with applicative idiom brackets. (I'm ignoring the fact that monad-embed doesn't support infix operators nor type classes, because they could be added.)

Another option would be to extend monad-embed to allow flavors to be either applicative functors or monads, and to only require that an expression's flavor be a monad if it contains strict do-bindings or do-actions. However, I haven't looked into this very much.

Q: In what order are side-effects performed?

monad-embed orders side effects according to the following rules:

These rules are, in a sense, arbitrary. If the first rule were reversed, so that f a would perform the side effects of a before the side effects of f, then monad-embed would still be a consistent and usable language.

(In C, the ordering in which function arguments are evaluated is implementation-dependent. It is ironic that a functional language has a more clearly defined ordering of side effects than an imperative language.)

Q: What happens with higher-order functions? For example, what would flip f a b do and why?

Evaluating flip f a b would perform the following side effects:

  1. If f itself has any side effects, then they are performed first. Note that there is a difference between an expression having side effects and an expression refering to a function that has side effects when applied; only the side effects from the expression f itself are performed here.
  2. The side effects from a and b are be performed, in that order.
  3. Any side effects from calling f on b are performed.
  4. Any side effects from calling the return value of f b on a are performed.

The reason why f, a, and b are evaluated in that order is because flip is a strict function which operates on a strict function and returns a strict function, so all of the arguments are evaluated strictly.

There is another flip-like function in the standard library, called zflip. The difference is that flip operates on a strict function and returns a strict function, while zflip operates on a lazy function and returns a lazy function:

flip :: (a :: *, b :: *, c :: *, m :: * -> *)
     => {m}
        (a -> b -> c) -> (b -> a -> c);

zflip :: (a :: *, b :: *, c :: *, m :: * -> *)
      => {m}
         ([a] -> [b] -> c) -> ([b] -> [a] -> c);

Depending on the definition of f', evaluating zflip f' [a] [b] might not perform the side effects of a or b at all, or it might perform them any number of times in any order. See this example program.

In general, you can work out how anything will behave by consulting the conversion rules.

Q: How are monad-embed and Disciple different?

Disciple allows functions to have side effects of modifying state or performing IO actions, but it does not express these actions via monads; the Disciple side effect system replaces the ST and IO monads.

monad-embed allows functions to have any side effect that can be expressed in a monad. The monad-embed side effect system is built on monads rather than replacing them. monad-embed code can be converted to Haskell code according to some simple rules; monad-embed is almost a syntactic sugar for Haskell.

Disciple allows destructive updates to any data structure; monad-embed does not.

In both Disciple and monad-embed, the type of a function restricts what side effects it can perform.

In both Disciple and monad-embed, functions are strict by default but can be made lazy if necessary. In Disciple, lazily evaluated code is not allowed to have side effects. In monad-embed, lazily evaluated code may have side effects; the side effects of a lazily evaluated chunk of code are evaluated every time the lazily evaluated value is used. (See this example program.)

(I'm not very familiar with Disciple; if I have made a mistake, please point it out.)