Monads.jl — Monadic Computation¶
Monads.jl provides a powerful, if relatively slow, implementation of monadic computation with several monads and combinators predefined. Monads.jl contains implementations of the identity, maybe, list, and state monads. It also offers Haskell-like syntactic sugar for chaining monadic computations with the @mdo macro.
Example¶
Using the list monad MList, we can perform guarded list comprehensions:
julia> @mdo MList begin
a <- MList(1:3)
b <- MList(1:3)
guard(a!=b)
return (a,b)
end
MList([(1,2), (1,3), (2,1), (2,3), (3,1), (3,2)])
This demonstrates the basic building blocks of a Julian monadic expression. @mdo opens the expression, followed by the name of the monad to compute in and a block with monadic computations. The <- operator is used to extract a value from a monad to use in a later step in the process. Combinators such as guard are automatically dispatched to the correct monad type, and the mreturn expression wraps the final result.
With the Maybe monad, we can immediately terminate a computation that produces nothing rather than throw an error.:
julia> @mdo Maybe begin
m <- Maybe(match(r"a", "bbbbac"))
return m.offset
end
Maybe{Int64}(5)
julia> @mdo Maybe begin
m <- Maybe(match(r"a", "bbbbc"))
return m.offset
end
Maybe{Nothing}(nothing)
Macros¶
-
@mdo(mtype, body)¶ Perform the series of monadic computations defined by
bodyin the monadmtype. While monadic computations can be performed by directly calling monad combinators, it is often more convenient to represent them in imperative form.@mdoallows you to represent the computation in a sugared form which omits repeated type information needed to correctly dispatch monad combinators.Within
@mdoblocks,mreturn,mzero,guard, andliftMgain superpowers; specifically, their first argument (which is a typeT<:Monad) may be omitted. The<-operator andreturnexpression are given alternate meanings which will be familiar to users of Haskell’sdosyntax.<-will extract the value of a monad to use in a further computation, andreturnbecomes an alternate spelling formreturn.
Types¶
Monad- The base monad type. New monads should be subtypes of this type, and must implement either
fmapandmbind, orjoin(the defaultmreturnis usually acceptable). Identity- The identity monad. While it isn’t very interesting, it might be a good reference if you are implementing your own monads.
Maybe{T}- The maybe monad. This monad can take either a non-
nothingvalue, ornothing. When its value becomesnothing, further computation will cease. State- The state monad.
MonadPlus- The base for MonadPlus types. A new monad deriving from this type should meet the requirements of a
Monad, and also must implementmzeroandmplus. MList- The list monad. The constructor expects a
Vector.
Methods¶
-
fmap(f, m)¶ Map the function
fover the monadm. This will generally speaking applyfto the contents ofm. Equivalent to:mbind(m) do x mreturn(M, f(x)) end
where
Mis the type of the monadm.
-
mbind(f, m)¶ The monad bind operation of the function
fto the monadm, equivalent tojoin(fmap(f, m)).
-
join(m)¶ Join should flatten one level of monadic structure, ending in at least one monadic wrapper, equivalent to
mbind(identity, m)
-
mreturn(M, val)¶ Monadic return should wrap a value in a monad. Usually equivalent to
M(val).
-
mcomp(g, f)¶ Composition of two monadic functions. Equivalent to
x -> mbind(g, f(x)).
-
mthen(k, m)¶ Sequencing of monadic actions. Equivalent to
mbind(_ -> k, m). Can also be spelled with the infix operator>>.
-
mzero(M)¶ The zero value of a MonadPlus
M. This should be the identity formplus. For instance, this is the empty list[]forMList.
-
mplus(m1, m2)¶ The addition operation for a MonadPlus.
-
guard(M, c)¶ When
M<:MonadPlus,guardfilters values based on the Boolean predicatec.
Implementing a monad¶
To implement your own monad, you will need to create a new type that is a subtype of either Monad or MonadPlus and implements either mbind and fmap, or join, each of which you will need to import from Monads. The methods you define should obey the following rules:
mbind(f, mreturn(M, a))) == f(a)
mbind((x) -> mreturn(M, x), m) == m
mbind(g, mbind(f, m)) == mbind((x) -> mbind(g, f(x)), m)
If your monad type is a subtype of MonadPlus, it should also define the additional functions mplus, which combines instances of the monad, and mzero, which is the identity under mplus. It should obey the following rules:
mbind(f, mzero) = mzero
mthen(mzero, v) = mzero
For more information, the definitive reference is the Typeclassopedia <http://www.haskell.org/haskellwiki/Typeclassopedia>_.