Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure macro that builds a higher order function from a function

Tags:

macros

clojure

I'm trying to write a macro that builds middleware akin to that used in compojure.

I want to be able to call:

(def-middleware plus-two [x]
  (+ 2 x))

And have the result look like:

(defn plus-two [f]
 (fn [x]
   (f (+ 2 x)))

I've got this far from reading on-line guides but it's not working out for me:

(defmacro def-middleware [fn-name args & body]
  '(defn ~fn-name [f]
     (fn ~args
       (f ~@body))))

Any help or a pointer to a better macro writing guide would be great, thanks.

like image 201
jdoig Avatar asked Jun 11 '12 19:06

jdoig


2 Answers

Let's see what macroexpand-1 gives us:

user=> (clojure.pprint/pprint (macroexpand-1 '(def-middleware plus-two [x] (+ 2 x))))
(defn
~fn-name
[f]
$
(fn ~args$ (f (clojure.core/unquote-splicing body))))

Not precisely what we're after! First things first: if you want to unquote things in a macro, you need to use "`" (the quasiquote/syntax-quote operator), not "'". Also, I'm not sure what you're after with those dollar signs, but it might be that you're going for the clojure's handy shortcut for using gensym for hygiene. But you need it immediately after each identifier you use it with (so [f#], not [f]$), and you need it on each occurrence of the identifier. And you don't need it with args. Putting all this together:

user=> (defmacro def-middleware [fn-name args & body] `(defn ~fn-name [f#] (fn ~args (f# ~@body))))
#'user/def-middleware
user=> (clojure.pprint/pprint (macroexpand-1 '(def-middleware plus-two [x] (+ 2 x))))
(clojure.core/defn
 plus-two
 [f__594__auto__]
 (clojure.core/fn [x] (f__594__auto__ (+ 2 x))))
nil
user=> 
like image 132
ben w Avatar answered Oct 03 '22 07:10

ben w


Perhaps this is just a macro exercise, but the specific example of defining a middleware function is not good - you lose a lot of the flexibility that the middleware concept gives you. For example, why should this evaluate (f (+ 2 x)) instead of (+ 2 (f x))? Both are sensible ways to wrap this function, and your syntax gives no way to describe the latter. Really just defining a function-returning-function is flexible, simple, and easy; this macro brings little to the table.

like image 23
amalloy Avatar answered Oct 03 '22 09:10

amalloy