Jason Basanese
Jason Basanese

Reputation: 710

How to avoid repetitive function parameters in clojure?

I have a series of functions that all do different things but are passed the same data. Let's say I have the following as a parameter vector.

[{:keys [x y vx vy x-min x-max y-min y-max] :or {x-min 0 x-max c-width y-min 0 y-max c-height}}]

Basically the type of thing you do not want to have to repeat more than once. One thought I had was to do something like.

(def func-args [x y z])
(defn func func-args (+ x y z))

Then I could re-use func-args if I had a similar function. However the first line of the previous example throws an error that symbol x cannot be resolved.

Upvotes: 3

Views: 183

Answers (2)

Alan Thompson
Alan Thompson

Reputation: 29958

Here is another way of doing it that accepts the function args spec vector as a parameter to the macro:

(defmacro defn-args
  [fn-name fn-args & forms]
  `(defn ~fn-name ~(eval fn-args) ~@forms))

(def square-args '[x] )    ; must quote [x] here
(defn-args my-square square-args (* x x))

(my-square 3) 
;=> 9

Upvotes: 2

Alex Miller
Alex Miller

Reputation: 70211

Quoting a vector will leave it's symbols unevaluated, however defn expects a literal vector in the definition, so this won't work like you're suggesting.

The easiest way to do this is to create a macro. Macros let you create your own syntax. This kind of (anaphoric) macro is discouraged in Clojure as it's easy to run into problems with macros that create symbols for you.

Something like:

(def cheight 100)
(def cwidth 100)
(defmacro defx [name & body]
  `(defn ~name [{:keys [~'x ~'y ~'vx ~'vy ~'x-min ~'x-max ~'y-min ~'y-max] 
                 :or {~'x-min 0 ~'x-max ~'c-width ~'y-min 0 ~'y-max ~'c-height}}]
     ~@body))

(defx func (+ x y))

(func {:x 1 :y 2}) ;; 3

Upvotes: 6

Related Questions