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.
monad-embed orders side effects according to the following rules:
f a
, the side effects of f
itself are performed first, followed by the side effects of a
, and then any side effects in the body of the function f
.a `f` b
are desugared to f a b
. Note that this means that the side effects of f
are evaluated first, even though it appears after a
in the source code.do { a; } then b
, the side effects of a
are evaluated before the side effects of b
. The same goes for expressions of the form do { v = a; } then b
.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.)
flip f a b
do and why?Evaluating flip f a b
would perform the following side effects:
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.a
and b
are be performed, in that order.f
on b
are performed.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.
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.)