Pure Danger Tech


Learning Clojure #12: condp

05 Mar 2010

I had a case today where I really wanted effectively a Java switch. cond seemed like overkill but then I stumbled on condp which is still more powerful than switch but pretty great for what I needed. The condp function has the following specification:

clojure.core/condp<br /> ([pred expr & clauses])


  • pred – is a predicate that takes two arguments
  • expr – is an expression to apply the predicate with
  • clauses – each clause consists of a test expression to be evaluated in pred along with expr and the result expression. OR, there is a three-part form of the clause that does the same, but then evaluates the result expression with the result of the predicate as an argument.

Additionally, there can be a single expression at the end that is executed as an “else” if no other tests evaluate to true. The most basic example would be something like this:

user=> (defn test-condp [x] 
(condp = x
0 "got 0"
1 "got 1"
(str "got " x)))
user=> (test-condp 0)
"got 0"
user=> (test-condp 1)
"got 1"
user=> (test-condp 2)
"got 2"
user=> (test-condp "foo")
"got foo"

Here the pred is = x and each expression will be evaluated in turn: (= 0 x) then (= 1 x) and finally the last expression if no test has evaluated to true.

The three-part form uses a special keyword :>> to set it apart and might look like this:

user=> (defn test-condp3 [k] 
    (condp  get k
        {:a 1, :b 2} :>> (partial + 3)
        {:c 3, :d 4} :>> (partial + 100)
user=> (test-condp3 :a)                        
user=> (test-condp3 :b)
user=> (test-condp3 :c)
user=> (test-condp3 :d)
user=> (test-condp3 :e)

Hopefully that’s clear – it was hard to come up with a great use case for this but I think this illustrates doing the lookup in the test and then passing the result onto the result expression, which avoids needing to re-access the map.

In my case, the function takes a string (read from elsewhere and passed here as value) and the sql-type (as defined in java.sql.Types) and does a conversion from the String into the appropriate value type:

(defn convert 
	(condp = sql-type
		Types/SMALLINT (Integer/valueOf value)
		Types/DECIMAL (Double/valueOf value)
		Types/DATE (parse-date value **DATE-FORMAT**)
		Types/TIMESTAMP (parse-date value **TIMESTAMP-FORMAT**)