Monad Operations

hymn.operations provide operations and macros for monad computations

Macros

do-monad [binding-forms expr]

macro for sequencing monadic computations, with automatic return

=> (require hymn.operations)
=> (import [hymn.types.maybe [Just]])
=> (do-monad [a (Just 41)] (inc a))
Just(42)
do-monad-m [binding-forms expr]

macro for sequencing monadic computations, a.k.a do notation in haskell

=> (require hymn.operations)
=> (import [hymn.types.maybe [Just]])
=> (do-monad [a (Just 41)] (m-return a))
Just(42)
do-monad-with [monad binding-forms expr]

macro for sequencing monadic composition, with said monad as default.

useful when the only binding form is :when, we do not know which monad we are working with otherwise

=> (require hymn.operations)
=> (import [hymn.types.maybe [maybe-m]])
=> (do-monad-with maybe-m [:when true] 42)
Just(42)
=> (do-monad-with maybe-m [:when false] 42)
Nothing

All do monad macros support :let binding, like this:

=> (require hymn.operations)
=> (import [hymn.types.maybe [Just]])
=> (defn half [x]
...  (do-monad
...    [:let [[two 2]]
...     a x
...     :let [[b (/ a two)]]]
...    b))
=> (half (Just 42))
Just(21.0)

All do monad macros support :when if the monad is of type MonadPlus.

=> (require hymn.operations)
=> (import [hymn.types.maybe [maybe-m]])
=> (defn div [a b] (do-monad-with maybe-m [:when (not (zero? b))] (/ a b)))
=> (div 1 2)
Just(0.5)
=> (div 1 0)
Nothing
monad-> [init-value &rest actions]

threading macro for monadic actions

=> (require hymn.operations)
=> (import [hymn.types.maybe [maybe-m]])
=> (def m-inc (maybe-m.monadic inc))
=> (def m-div (maybe-m.monadic /))
=> ;; threading macro for monadic actions
=> (monad-> (maybe-m.unit 99) m-inc (m-div 5) (m-div 2))
Just(10.0)
=> ;; is equivalent to
=> (do-monad-m [a (maybe-m.unit 99) b (m-inc a) c (m-div b 5)] (m-div c 2))
Just(10.0)
monad->> [init-value &rest actions]

threading tail macro for monadic actions

=> (require hymn.operations)
=> (import [hymn.types.maybe [maybe-m]])
=> (def m-inc (maybe-m.monadic inc))
=> (def m-div (maybe-m.monadic /))
=> ;; threading tail macro for monadic actions
=> (monad->> (maybe-m.unit 4) m-inc (m-div 25) (m-div 100))
Just(20.0)
=> ;; is equivalent to
=> (do-monad-m [a (maybe-m.unit 4) b (m-inc a) c (m-div 25 b)] (m-div 100 c))
Just(20.0)
m-for [[n seq] &rest expr]

macro for sequencing monadic actions

=> (require hymn.operations)
=> ;; with simple monad, e.g. maybe
=> (import [hymn.types.maybe [maybe-m]])
=> (m-for [a (range 3)] (maybe-m.unit a))
Just([0, 1, 2])
=> ;; with reader monad
=> (import [hymn.types.reader [<-]])
=> (def readers
...  (m-for [a (range 5)]
...    (print "create reader" a)
...    (<- a)))
create reader 0
create reader 1
create reader 2
create reader 3
create reader 4
=> (.run readers [11 12 13 14 15 16])
[11, 12, 13, 14, 15]
=> (.run readers "abcdefg")
['a', 'b', 'c', 'd', 'e']
=> ;; with writer monad
=> (import [hymn.types.writer [tell]])
=> (.execute (m-for [a (range 1 101)] (tell a)))
5050
m-when [test mexpr]

conditional execution of monadic expressions

with-monad [monad &rest exprs]

provide default function m-return as the unit of the monad

=> (require hymn.operation)
=> (import [hymn.types.maybe [maybe-m]])
=> (with-monad maybe-m (m-when (even? 1) (m-return 42)))
Just(None)
=> (with-monad maybe-m (m-when (even? 2) (m-return 42)))
Just(42)
monad-comp [expr binding-forms &optional condition]

different syntax for do-monad, in the style of list/dict/set comprehensions, the condition part is optional and can only be used with MonadPlus as in do-monad

=> (require hymn.operations)
=> (import [hymn.types.maybe [Just]])
=> (monad-comp (+ a b) [a (Just 1) b (Just 2)])
Just(3)
=> (monad-comp (/ a b) [a (Just 1) b (Just 0)] (not (zero? b)))
Nothing
=> (import [hymn.types.list [list-m]])
=> (list (monad-comp (/ a b) [a (list-m [1 2]) b (list-m [4 8])]))
[0.25, 0.125, 0.5, 0.25]
=> (list (monad-comp (/ a b) [a (list-m [1 2]) b (list-m [0 1])] (not (zero? b))))
[1.0, 2.0]

Reader Macros

^ [f]

lift() reader macro, #^f is expanded to (lift f)

=> (require hymn.operations)
=> (import [hymn.types.maybe [Just Nothing]])
=> (#^+ (Just 1) (Just 2))
Just(3)
=> (#^+ (Just 1) Nothing)
Nothing
= [value]

reader macro for m-return, the unit inside do-monad macros, #=v is expanded to (m-return v)

=> (require hymn.operations)
=> (import [hymn.types.maybe [Just maybe-m]])
=> (do-monad-with maybe-m [a #=1 b #=2] (+ a b))
Just(3)
=> (do-monad-m [a (Just 1)] #=(inc a))
Just(2)

Operation on Monads

hymn.operations.k_compose(*monadic_funcs)

right-to-left Kleisli composition of monads.

<=<

alias of k_compose()

=> (import [hymn.operations [k-compose <=<]])
=> (import [hymn.types.maybe [Just Nothing]])
=> (defn m-double [x] (if (numeric? x) (Just (* x 2)) Nothing))
=> (defn m-inc [x] (if (numeric? x) (Just (inc x)) Nothing))
=> (def +1*2 (k-compose m-double m-inc))
=> (+1*2 1)
Just(4)
=> (def *2+1 (<=< m-inc m-double))
=> (*2+1 2)
Just(5)
=> (*2+1 "two")
Nothing
hymn.operations.k_pipe(*monadic_funcs)

left-to-right Kleisli composition of monads.

>=>

alias of k_compose()

=> (import [hymn.operations [k-pipe >=>]])
=> (import [hymn.types.maybe [Just Nothing maybe]])
=> (def m-int (maybe int))
=> (defn m-array [n] (if (> n 0) (Just (* [0] n)) Nothing))
=> (def make-array (k-pipe m-int m-array))
=> (make-array 0)
Nothing
=> (make-array 3)
Just([0, 0, 0])
=> (def make-array (>=> m-int m-array))
=> (make-array 2)
Just([0, 0])
hymn.operations.lift(f)

promote a function to a monad

=> (import [hymn.operations [lift]])
=> (import [hymn.types.maybe [Just]])
=> (def m+ (lift +))
=> (m+ (Just 1) (Just 2))
Just(3)
hymn.operations.m_map(mf, seq)

map monadic function mf to a sequence, then execute that sequence of monadic values

m-map

alias of m_map()

=> (import [hymn.operations [m-map]])
=> (import [hymn.types.maybe [maybe-m]])
=> (m-map maybe-m.unit (range 5))
Just([0, 1, 2, 3, 4])
=> (m-map (maybe-m.monadic inc) (range 5))
Just([1, 2, 3, 4, 5])
=> (import [hymn.types.writer [tell]])
=> (.execute (m-map tell (range 1 101)))
5050
hymn.operations.replicate(n, m)

perform the monadic action n times, gathering the results

=> (import [hymn.operations [replicate]])
=> (import [hymn.types.list [list-m]])
=> (list (replicate 2 (list-m [0 1])))
[[0, 0], [0, 1], [1, 0], [1, 1]]
hymn.operations.sequence(m_values)

evaluate each action in the sequence, and collect the results

=> (import [hymn.operations [sequence]])
=> (import [hymn.types.writer [tell]])
=> (.execute (sequence (map tell (range 1 101))))
5050