Clojure and me has moved.

Saturday, January 31, 2009

Shadowing global names

This post has moved, go to its new location
A nice article on Clojure that gives one wrong tip: to use #'var-name to access a shadowed global.
Don't do that! You'd better use namespace-where-defined-or-alias/var-name.
There are several reasons not to use #'var-name:
  • it doesn't work if the value of your var is not invokable,
  • it evaluates to something different: it returns the var an not its value. It happens that vars proxy function calls to their values but a var can be rebound:
    (def a (map str (range 10)))
    (def b (map #'str (range 10)))
    (take 5 a)
    ("0" "1" "2" "3" "4")
    (take 5 b)
    ("0" "1" "2" "3" "4")
    (binding [str identity] (doall a))
    ("0" "1" "2" "3" "4" "5" "6" "7" "8" "9")
    (binding [str identity] (doall b))
    ("0" "1" "2" "3" "4" 5 6 7 8 9)
NB: @#'var-name is better than #'var-name but, really, just use the namespaced symbol.

Tuesday, January 27, 2009

Living on the bleeding edge

This post has moved, go to its new location
You can try clojure libs straight from the cloud:
(add-classpath "http://clojure-contrib.svn.sourceforge.net/viewvc/clojure-contrib/trunk/src/") ; sourceforge
(add-classpath "http://github.com/Lau-of-DK/clojureql/raw/master/src/"); github
(require '[clojure.contrib.duck-streams :as duck])
(require '[dk.bestinclass.clojureql :as ql])

Disclaimer:

  • add-classpath is not meant to be used anywhere but at the repl,
  • you'd better trust what you download,
  • if you are unlucky you can get an inconsistent snapshot,
  • use at your own risk!

Tuesday, January 20, 2009

Bindings and send

This post has moved, go to its new location
This morning on #clojure erohtar asked:
what is the best way to bind vars to a value when sending to an agent?

I don't know if it's the best way but it's mine:
(defmacro capture-and-send
"Capture the current value of the specified vars and rebind
them on the agent thread before executing the action."
[vars agent action & args]
(let [locals (map #(gensym (name %)) vars)]
`(let [~@(interleave locals vars)
action# (fn [& args#]
(binding [~@(interleave vars locals)]
(apply ~action args#)))]
(send ~agent action# ~@args))))
;; usage:
(capture-and-send [*out*] a f b c)

I post it here because erohtar needed it, I needed it once so others may need it.

Monday, January 19, 2009

Enlive: yet another HTML templating library

This post has moved, go to its new location
[UPDATE] I have rewritten Enlive, this posts doesn't work with the actual Enlive.
Enlive is a selector based templating library.
Its main design goal is to decouple html and presentation code, that's why Enlive templates are plain old html files (it will ease roundtripping with designers).
In code, you have to declare where (using CSS-like selectors) and how (using clojure code) to alter the html template.
The resulting templating functions are compiled (the html tree isn't transformed at runtime) and yields a seq of strings.

Here is an example:
(deftemplate microblog-template "net/cgrand/enlive_html/example.html" [title posts]
[:title] title
[:h1] title
[:div.no-msg] (when-not (seq posts) ~(html/show))
[:div.post] (for [{:keys [title body]} posts]
~(at
[:h2] title
[:p] body)))

;; at the repl:
net.cgrand.enlive-html.examples=> (apply str (microblog-template "Hello user!"
[{:title "post #1"
:body "hello with dangerous chars: <>&"}
{:title "post #2"
:body "dolor ipsum"}]))

"<html><head><title>Hello user!</title></head>
<body><h1>Hello user!</h1>
<div class=\"post\"><h2>post #1</h2>
<p>hello with dangerous chars: &lt;&gt;&amp;</p></div>
<div class=\"post\"><h2>post #2</h2>
<p>dolor ipsum</p></div></body></html>"
(NB: manually edited to add linebreaks.)

(Disclaimer: original idea by Ozzilee)

Sunday, January 11, 2009

Functionally growing a tree

This post has moved, go to its new location
I was trying to write a restartable parser in Clojure when it occured to me that I was doing it wrong by not using clojure.zip to build the parse tree.
Update: follow-up

Thursday, January 8, 2009

try-or, or-try, try-else or else-try?

This post has moved, go to its new location
I can't decide which name is best for this macro:
(defmacro try-or
"Evaluates exprs one at a time, from left to right. If a form returns a
value, this value is returned. If a form throws an exception, the next
form is evaluated.
If the last form throws an exception, the exception isn't caught."
([] nil)
([form] form)
([form & forms]
`(try
~form
(catch Exception e#
(try-or ~@forms)))))

Monday, January 5, 2009

Recursive seqs

This post has moved, go to its new location
Recursively defined sequences are pretty but difficult to get right when you don't want to assign the sequence to a var. Here is a couple of macros to ease recursive definitions.
(defmacro rec-cat 
"Similar to lazy-cat but binds the resulting sequence using the supplied
binding-form, allowing recursive expressions. The first collection
expression must not be recursive and must return a non-nil seq."
[binding-form expr & rec-exprs]
`(let [rec-rest# (atom nil)
result# (lazy-cat ~expr (force @rec-rest#))
~binding-form result#]
(swap! rec-rest# (constantly (delay (lazy-cat ~@rec-exprs))))
result#))

(defmacro rec-cons
"Similar to lazy-cons but binds the resulting sequence using the supplied
binding-form, allowing recursive expressions. The first expression must
not be recursive and must return a non-nil seq."
[binding-form expr & rec-exprs]
`(let [rec-rest# (atom nil)
result# (lazy-cons ~expr (force @rec-rest#))
~binding-form result#]
(swap! rec-rest# (constantly (delay (lazy-cat ~@rec-exprs))))
result#))

Examples:

Natural numbers (just like (iterate inc 0)):
(rec-cons naturals 0 (map inc naturals))
fibonnaci sequence:
(rec-cat fibs [0 1] (map + fibs (rest fibs)))
Chouser's cute reduction:
(defn reduction
"Returns a lazy seq of the intermediate values of the reduction (as
per reduce) of coll by f, starting with init."
([f coll]
(if (seq coll)
(rec-cons reductions (first coll) (map f reductions (rest coll)))
(cons (f) nil)))
([f init coll]
(rec-cons reductions init (map f reductions coll))))