Clojure refactoring: flattening reduces

mistakes — cgrand, 19 January 2010 @ 12 h 48 min

This morning I wrote some code which looked like:

(reduce (fn [acc x]
          (reduce (fn [acc y]
                    (reduce f acc y)) acc x)) init xs)

(it was slightly more complex with some filtering and destructuring thrown in for good measure).

I wasn’t happy with those nested reduces and it occured to me that I could refactor it to use a single one:

(reduce f init (for [x xs, y x, z y] z))

Now that reads better!

Vicious bug

mistakes — cgrand, 22 April 2009 @ 11 h 44 min
What’s wrong with this code (do (defmacro foobar [x] (doto x println)) (foobar (+ 1 1)))?
Nothing.
Run it again.
The first time, foobar is treated as a function (its argument has been evaluated) because foobar is unknown before the whole top-level expression is compiled.
The second time, the var named foobar preexists and is a macro, (foobar (+ 1 1)) is expanded.

This behaviour bit me while using with-test to test macros. It’s the kind of bug that goes unnoticed while developing incrementally and evaluating in the same REPL, it was waiting for me to start a fresh REPL.

Functionally growing a tree

mistakes — cgrand, 11 January 2009 @ 19 h 32 min

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

Misconception

mistakes — cgrand, 8 July 2008 @ 10 h 01 min

I thought that there was some kind of doall in apply to force evaluation of the arguments seq but it’s not the case when the applied function is variadic.

Several mistakes (updated)

mistakes — cgrand, 18 June 2008 @ 16 h 05 min

In the previous post, there were several mistakes. They are all fixed by now, except one:

(defn top [n h]
  (take n (sort #(- (val %2) (val %1)) h)))

Yup, this code is buggy: it is intended to sort hash entries by descending val order (values are numbers) and it breaks on large data sets.
With large data sets, some values get big and doesn’t fit into an int anymore while some stay small thus the difference of such two numbers doesn’t fit into an int while Comparator.compare must return an int: overflow.
Here is one way to fix that:

(defn top [n h]
  (take n (sort #(.compare clojure.lang.Numbers (val %2) (val %1)) h)))

I have hope that there will be a better way to fix that in a near future.

(defn top [n h]
  (take n (sort #(compare (val %2) (val %1)) h)))

How to genclass a servlet

mistakes — cgrand, 4 June 2008 @ 15 h 45 min
This is an old post, refers to Clojure.org for details on how gen-class works now.

Although generating a servlet class is a canonical example of using Clojure’s genclass I had never do this — I’ve used a handmade ClojureServlet which predates genclass.
Today, as I was starting a new project, I decided to get rid of this legacy ClojureServlet and to generate a brand new one.

Right at the end of genclass.clj there’s the following example:

(clojure/gen-and-save-class "/Users/rich/dev/clojure/gen/" 
 'org.clojure.ClojureServlet 
 :extends javax.servlet.http.HttpServlet)

I changed the path, created the directory structure (gen-and-save-class doesn’t create the org/clojure/ directories) and ran this code (NB: servlet-api.jar must be on the classpath). Et voilà, org/clojure/ClojureServlet.class!

Now to the Clojure code, I created org/clojure/ClojureServlet.clj to define the methods and launched Tomcat to test the servlet. It crashed in the init method.
In org/clojure/ClojureServlet.clj I have:

(in-ns 'org.clojure.ClojureServlet)
(clojure/refer 'clojure)

(defn init [this config]
  ;censored
)

Cause: init does not call super.init(ServletConfig) as GenericServlet mandates it. Hopefully GenericServlet provides an init() which is easier to override, so I changed the code to:

(in-ns 'org.clojure.ClojureServlet)
(clojure/refer 'clojure)

(defn init-void [this]
  ;censored, again
)

which override only the init method taking no arguments. The bug was fixed.

To override a specific signature, define a var named methodName-firstArgClassName-secondClassName... (e.g. init-ServletConfig) or methodName-void if no arguments.

Multimethods aren’t magic (nor mutable)

mistakes — cgrand, 28 May 2008 @ 17 h 55 min

What does this code display?

(defmulti foo first)
(def print-foo (comp println foo))
(defmethod foo :a [x]
  "it's a 'a'!")
(print-foo [:a])

Nothing, it raises an error: it doesn’t know what to do with the dispatch value :a. The value of foo is captured when comp is called.

The thing to keep in mind is that defmethod mutates the var, not the multimethod itself — which is of course immutable. The solution is to pass the var instead of the multimethod to comp:

(defmulti foo first)
(def print-foo (comp println #'foo))
(defmethod foo :a [x]
  "it's a 'a'!")
(print-foo [:a]) ; prints "it's a 'a'!"

Update or maybe it’s a code smell that you should use a macro instead of a function.

Update 2 Clojure’s father (Rich Hickey) says : the correct thing to do is use the var.

(c) 2024 Clojure and me | powered by WordPress with Barecity