Monday, January 11, 2010

clojure.core/str makes sense now

clojure.core/str makes sense

While reading excellent Programming Clojure by Stuart Halloway, instead just reading I started looking into how things are done by Clojure itself.
Yesterday it took me some time before I understood how things are done by clojure.core/str. This post is documentation of my findings.
Following modification of said function was very helpful in understanding how it works:
:tag metadata key is a symbol naming a class or a Class object that indicates the Java type of the object in the var, or its return value if the object is a fn..
Next are three parameters body pairs.
([] "")
No arguments (arity 0) will result in "" (empty string)

([#^Object x]
(if (nil? x) "" (. x (toString))))
One argument call (arity 1) will call java.lang.Object.toString method.
#^Object is a Type Hint.
if should not need much explanation.

([x & ys]
((fn [#^StringBuilder sb more]
(if more
(recur (. sb (append (str (first more)))) (next more))
(str sb)))
(new StringBuilder #^String (str x)) ys)))
This is where it starts to be interesting.
First unnamed function is defined that accepts StringBuilder sb and more params.
If more is true, that is not nil or false than call recursively self with new parameters
  • sb with appended result of calling str with first element of more,
  • more with elements after first.
If more is false, that in our context will happen only when more is nil, call sb's toString.
Next just defined function is getting called with new StringBuilder constructed from x as sb and ys as more.

It took me some time to realize that ys are turning into more.

So there you have it clojure.core/str explained.


  1. Hey,

    Thanks for the write up. I think its very interesting to walk through core.clj every once in a while - Every now and then you learn something surprising.

    I'm genuinely looking forward to your examination of 'for' :)


  2. Hi, one tiny complaint. The main column of the blog is really narrow and the code examples end up wrapping. That makes things a bit hard to read. Also the fonts jump from small to large which again hurts readability.

  3. Hi Glen,

    Thank you for feedback.
    Updated css and made font size difference smaller.


  4. Nice article; clojure.core/str's implementation is not trivial, and it's good to understand how it is implemented. I'd like to point out, though, that in your mystr function, three times you needlessly wrap code in a do form. Because they're in a function definition, this:
    ([] (do (println "0:")
    Can become just:
    ([] (println "0:") "")
    You can pad that with more whitespace if you wish.

  5. Thanks for your feedback, I've already updated gist with your advice :)
    Always looking forward to makes things simpler,