Simple Macro
I recently had a requirement for a version of cond that took a predicate expression and passed the clause as a parameter. While condp was close it didn’t really match what I was trying to do. Macros are still new to me so this was a good learning experience.
Here’s an example of the code I wished to write:
(conde app/key-pressed? :up (move-player state 1 -1) :down (move-player state 1 1))
Alternatively the predicate can be a form, the clause is added to the end of the form. For example:
(conde (= 2) 1 (print "was one") 2 (print "was two"))
Since cond is almost what I wanted I took that code and altered it to get the following:
(defmacro conde "Takes a predicate form and a set of test/expr pairs. It evaluates each test one at a time against the predicate. If a test returns logical true, conde evaluates and returns the value of the corresponding expr and doesn't evaluate any of the other tests or exprs. (cond) returns nil." {:added "1.0"} [pred & clauses] (when clauses (let [pred (if (not (seq? pred)) (list pred) pred)] (list 'if `(~@pred ~(first clauses)) (if (next clauses) (second clauses) (throw (IllegalArgumentException. "cond requires an even number of forms"))) (cons 'yourns/conde (cons pred (next (next clauses))))))))
In the end the changes are pretty simple but it took me a bit of working out at the time. I’m just not used to macro programming yet. So some questions to the lazy web:
Firstly: can condp be used to achieve the same thing?
Secondly: the code checks whether the predicate is a sequence, this seems a little awkward. Is there a better way of doing this?
This seems to work (using condp):
(defmacro condd [pred & clauses]
`(letfn [(predd# [test-expr# expr#]
(->> test-expr# ~pred))]
(condp predd# nil
~@clauses)))