Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure macro to create a synonym for a function

Tags:

macros

clojure

Probably an easy one for anyone who actually knows how to write macros in any Lisp. I want to be able to define synonyms for function names. I've been copy-and-paste hacking core.clj to do this, but I don't want to be such a dunce forever! It seems obvious a macro that rewrites the call to a synoym-function into a call to the original function is the right way to do it.

like image 363
Al Chou Avatar asked Aug 22 '09 04:08

Al Chou


2 Answers

If I understand your question, there's an easier way: def the new symbol to the old function.

user=> (def foo +)
#'user/foo 
user=> (foo 1 2) 
3

The performance of def also outperforms the macro approach:

(defmacro foo2 [& args]
  `(+ ~@args))

foo2 is then effectively an alias for + and behaves exactly the same way (being rewritten as +) except for the restrictions that are placed on using macros where a value must be returned.

If you want the behavior of the "alias" to be exactly the same as that of the original function (callable in the same contexts as well) then you need to use def to rename the function.

user=> (def foo +)

user=> (defn foo1 [& args]
         `(+ ~@args))

user=> (defmacro foo2 [& args]
         `(+ ~@args))

user=> (time (dotimes [n 1000000] (foo 1 n)))
"Elapsed time: 37.317 msecs"

user=> (time (dotimes [n 1000000] (foo1 1 n)))
"Elapsed time: 292.767 msecs"

user=> (time (dotimes [n 1000000] (foo2 1 n)))
"Elapsed time: 46.921 msecs"
like image 132
G__ Avatar answered Oct 16 '22 04:10

G__


Macros are faster now

I have embarked on the (most assuredly foolish) task of renaming some of Clojure's core functions in one of my projects. I've been having great fun with it (fn becomes λ, loop becomes , etc.), but I found myself very curious about performance. Greg's excellent answer from five years ago is now, I think, partially wrong. I'm using Clojure 1.5.1.

For starters:

user=> (defn foo1 [& args] `(+ ~@args))
#'user/foo1
user=> (foo1 1 2 3 4)
(clojure.core/+ 1 2 3 4)

That's definitely not what you want. Moreover, it appears that now macros are definitely the fastest option. I duplicated Greg's experiments and got very different results. The times you see below are each the best of ten runs:

user=> (def foo +)
#'user/foo
user=> (defn foo1 [& args] (apply + args))
#'user/foo1
user=> (defmacro foo2 [& args] `(+ ~@args))
#'user/foo2
user=> (time (dotimes [n 1000000] (+ 1 n)))
"Elapsed time: 53.401812 msecs"
user=> (time (dotimes [n 1000000] (foo 1 n)))
"Elapsed time: 135.675486 msecs"
user=> (time (dotimes [n 1000000] (foo1 1 n)))
"Elapsed time: 494.770352 msecs"
user=> (time (dotimes [n 1000000] (foo2 1 n)))
"Elapsed time: 53.509264 msecs"

Also, I think the difference between the methods becomes insignificant as the function does more. This is the experiment I had run originally in which I found no difference between them:

user=> (defmacro α [& body] `(reduce ~@body))
#'user/α
user=> (defn β [& body] (apply reduce body))
#'user/β
user=> (def γ reduce)
#'user/γ
user=> (time (dotimes [n 10000] (reduce + (range n))))
"Elapsed time: 5466.920266 msecs"
user=> (time (dotimes [n 10000] (α + (range n))))
"Elapsed time: 5474.532622 msecs"
user=> (time (dotimes [n 10000] (β + (range n))))
"Elapsed time: 5491.337517 msecs"
user=> (time (dotimes [n 10000] (γ + (range n))))
"Elapsed time: 5456.271967 msecs"

Finally, what you're looking for might be defalias from clojure.contrib.def. I have no experience with it.

like image 30
galdre Avatar answered Oct 16 '22 04:10

galdre