The power of Lisp macros

(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at: lawrence@krubner.com, or follow me on Twitter.

Lisp macros allow you to take some code and re-write as different code (among other things). With most languages if you want new language features then you have to wait till the dev team adds it to the core, but with Lisp, anyone can add new features to the language, just by writing macros. An interesting example is offered by Baishampayan Ghose:

To me pattern matching is the bee’s knees :) If you have a lot of
functions where you check the input for some certain type of value and
then perform some action depending on the value(s), then pattern
matching can be very useful. It’s used extensively in Erlang and
Haskell among others.
For example, say you have a function `decide` –
(defn decide
“A very simple example using cond”
[x y]
(cond
(= x :foo) (do-foo y)
(= x :bar) (do-bar y)
:else (do-default y)))
I expect two arguments x & y and depending on the value of `x`, I
would like to call the functions `do-foo`, `do-bar` or `do-default`
with the value of `y`.
See how the code is riddled with the conditional checks and my
business logic (the part I care the most about). Using
pattern-matching, I can write the above code a bit more succinctly
like this –
(defn decide
“A very simple example using match”
[x y]
(match [x y]
[:foo arg] (do-foo arg)
[:bar arg] (do-bar arg)
[_ arg] (do-default arg)))
The above code is *much* clearer and conveys the exact logic that I am
trying to implement. Internally `match` will rewrite the above to an
optimised bit of code which uses `cond`.
If I am feeling adventurous, I can write a simple macro which will let
me define the same function in an even more succinct manner like this

(defm decide
“A very simple example”
([:foo arg]
(do-foo arg))
([:bar arg]
(do-bar arg))
([_ arg]
(do-default arg)))
A sample implementation of the `defm` macro used above could be
something like this (UNTESTED!) –
(defmacro defm
[name & fdecl]
(let [m (if (string? (first fdecl))
{:doc (first fdecl)}
{})
fdecl (if (string? (first fdecl))
(next fdecl)
fdecl)
argcount (count (ffirst fdecl))
args (vec (repeatedly argcount (partial gensym “arg__”)))
body (apply concat
(for [decl fdecl]
(if (nnext decl)
(cons (first decl) (list (cons `do (next decl))))
decl)))]
`(defn ~name
~m
~args
(match ~args
~@body))))
It’s even more awesome when you bring in complex maps, etc. into the picture.
I personally believe that David and Ambrose are doing some incredible
work with match & core.logic; these two projects will prove be
extremely useful in the future.
Regards,
BG

Post external references

  1. 1
    http://groups.google.com/group/clojure/browse_thread/thread/8aa213ce127c5aca?pli=1
Source