Petrus Theron
Petrus Theron

Reputation: 28837

Writing a Clojure #[...] Vector Function Macro

I want to write a Clojure vector macro form #[...] I can use like #[(inc %1) %2] that is equivalent to (fn [a b] [(inc a) b]) or #(vector (inc %1) %2).

Is this a special data reader, or can this be written with defmacro?

Upvotes: 0

Views: 204

Answers (1)

Carcigenicate
Carcigenicate

Reputation: 45750

Just for a laugh, I wrote up what is probably the closest you're going to come to a solution. It doesn't define a reader macro, but it does define a macro that almost allows for the syntax you're looking for.

I had to reinvent the wheel a bit to allow for % style implicit parameters since I couldn't get abusing #() to work:

(defn arg-list
  "Returns a lazy-seq of (%, %2, %3, ...) strings."
  ([] (arg-list 1))
  ([n] (lazy-seq
         (cons (str "%" (if (> n 1) n))
               (arg-list (inc n))))))

(defmacro lambda
  "Use as you would #(). (map (lambda (* % 2)) [1, 2, 3])"
  [& body]
  (let [arg-syms (->> (arg-list) (take 5) (mapv symbol))]
    `(fn [& ~arg-syms]
       ~@body)))

This defines a regular macro that mimics the #() reader macro. It evaluates to a variadic function that has the first 5 arguments in the list bound to "percentage" symbols. It allows for % style parameters:

(mapv (lambda (* % %2)) [1 2 3] [1 5 9])
[1 10 27]

Then, you can just use that to define a macro that almost does what you're looking for:

(defmacro vec-f [& elems]
  `(lambda (vec ~(vec elems))))

(mapv (vec-f (inc %) %2) [1 2 3] [1 5 9])
[[2 1] [3 5] [4 9]]

But I can't say that I recommend its use.

  • It doesn't lend itself to readability.

  • Instead of throwing ArityExceptions, the missed arguments will just default to nil via destructuring

  • It only allows up to %5; although that's a hard-coded limit that can be increased. Using the current "var-arg method", I can't allow for an arbitrary number of parameters. Trying to realize an infinite list at compile time will obviously end in tears.

As a proof of concept though, it shows what you're after is almost possible.

You should just use

#(vector (inc %1) %2)

instead, or, as suggested in the comments

#(do [(inc %1) %2]) 

Upvotes: 1

Related Questions