I’m back to algo.monads.
I can’t remember how much time I’ve already devoted to the library and the topic of monads in general, but I’m sure it took me already a couple of weeks and still whenever I’m back to algo.monads I find something interesting.
Today I thought I found a bug in m-lift macro and even reported an issue to Konrad Hinsen (the author of the library), but when I read the article A Monad Tutorial For Clojure Programmers (Part 1) I knew I was wrong.
I just badly needed the macroexpand-1 function. I should’ve remembered to give it a shot before I ran the alarm!
The m-lift macro “Converts a function f of n arguments into a function of n monadic arguments returning a monadic value.” I thought it was so easy to comprehend, but was so much mistaken.
Have a look at the REPL session below.
$ lein2 version
Leiningen 2.0.0-SNAPSHOT on Java 1.7.0-u10-b09 OpenJDK 64-Bit Server VM
jacek:~/sandbox
$ lein2 repl
nREPL server started on port 54046
REPL-y 0.1.2
Clojure 1.4.0
Exit: Control+D or (exit) or (quit)
Commands: (user/help)
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
(user/sourcery function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Examples from clojuredocs.org: [clojuredocs or cdoc]
(user/clojuredocs name-here)
(user/clojuredocs "ns-here" "name-here")
user=> (use '[cemerick.pomegranate :only (add-dependencies)])
nil
user=> (add-dependencies :coordinates '[[org.clojure/algo.monads "0.1.3-SNAPSHOT"]])
{[org.clojure/clojure "1.3.0"] nil, [org.clojure/tools.macro "0.1.0"] nil, [org.clojure/algo.monads "0.1.3-SNAPSHOT"] #{[org.clojure/tools.macro "0.1.0"] [org.clojure/clojure "1.3.0"]}}
user=> (require '(clojure.algo [monads :as m]))
nil
user=> (doc m/m-lift)
-------------------------
clojure.algo.monads/m-lift
([n f])
Macro
Converts a function f of n arguments into a function of n
monadic arguments returning a monadic value.
nil
user=> (m/m-lift 1 (fn [v] v))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: m-bind in this context, compiling:(NO_SOURCE_PATH:1)
See the RuntimeException? That’s where I thought I found a bug. I thought the m-bind should already been defined, but it was not. And it is on purpose – the m-bind is a part of a monad and it’s up to the monad to register it.
(clojure.core/fn [mv_5837] (m-bind mv_5837 (fn [x_5838] (m-result ((fn [v] v) x_5838)))))
It was when I realized I needed a monad. There are a few available, with identity-m being the simplest one.
(defmonad identity-m
"Monad describing plain computations. This monad does in fact nothing
at all. It is useful for testing, for combination with monad
transformers, and for code that is parameterized with a monad."
[m-result identity
m-bind (fn m-result-id [mv f]
(f mv))
])
nil
user=> (m/with-monad m/identity-m (m/m-lift 1 (fn [v] v)))
#<user$eval5844$fn__5847 user$eval5844$fn__5847@3330fadd>
user=> (macroexpand-1 '(m/with-monad m/identity-m (m/m-lift 1 (fn [v] v))))
(clojure.core/let [name__5505__auto__ m/identity-m m-bind (:m-bind name__5505__auto__) m-result (:m-result name__5505__auto__) m-zero (:m-zero name__5505__auto__) m-plus (:m-plus name__5505__auto__)] (clojure.tools.macro/with-symbol-macros (m/m-lift 1 (fn [v] v))))
As you can see m-bind and m-result (the two mandatory monad’s functions) are already bound and available. That’s how monads are implemented in algo.monads and with no monad there’s no m-bind and m-result. Simple, isn’t it?
I think I’ll now remember. Sorry for bothering.
