Обрабатывать макрос Clojure как функцию - PullRequest
15 голосов
/ 03 апреля 2011

Как я могу заставить макрос Clojure действовать как функцию, чтобы я мог передать его в качестве аргумента, например? Я ожидаю, что придется как-то обернуть его.

Я бы не ожидал, что завернутая версия будет вести себя точно так же, как и исходный макрос (различия между вызовом по имени и вызовом по значению), но это было бы полезно в некоторых случаях, когда это было не важно.

Ответы [ 3 ]

21 голосов
/ 04 апреля 2011

Если я правильно вас понимаю, вы можете просто обернуть это в функцию.

Рассмотрим эту (глупую) реализацию функции возведения в квадрат как макрос:

(defmacro square [x]
  (list * x x))

Передачакак аргумент, он не будет работать, как вы знаете:

user=> (map square [1 2 3 4 5])
java.lang.Exception: Can't take value of a macro: #'user/square (NO_SOURCE_FILE:8)

Но его обертывание в функции поможет:

user=> (map #(square %) [1 2 3 4 5])
(1 4 9 16 25)

В качестве альтернативы (и немного больше)зло), вы могли бы сделать другой макрос для более общего переноса:

(defmacro make-fn [m] 
  `(fn [& args#] 
    (eval `(~'~m ~@args#))))

user=> (map (make-fn square) [1 2 3 4 5])
(1 4 9 16 25)

Я бы придерживался обычного переноса функции и пропустил этот последний взлом!:)

5 голосов
/ 04 апреля 2011

Существует опасный устаревший макрос, который вы никогда не должны использовать. :-P

http://clojure.github.com/clojure-contrib/apply-macro-api.html

4 голосов
/ 01 мая 2014

Для сумасшедшего макроса make-fn , как насчет приведенного ниже? Это должно сработать, и, надеюсь, легче понять.

(defmacro make-fn [m] 
 `(fn [& args#]
    (eval 
      (cons '~m args#))))
...