leontalbot
leontalbot

Reputation: 2543

Is it good to avoid macro in this example?

I read that data > functions > macros

Say you want to evaluate code in a postfix fashion.

Which approach would be better?

;; Macro

(defmacro reverse-fn [expression]
  (conj (butlast expression) (last expression)))

(reverse-fn ("hello world" println))
; => "hello world"


;; Function and data

(def data ["hello world" println])

(defn reverse-fn [data] 
  (apply (eval (last data)) (butlast data)))

(reverse-fn ["hello world" println])
; => "hello world"

Thanks!

Upvotes: 1

Views: 106

Answers (4)

Leon Grapenthin
Leon Grapenthin

Reputation: 9266

If you require different evaluation behavior for any kind of data in your code macros are your best choice because they can transform unevaluated data at compile time to the code you'd like to be evaluated instead.

Clojure has a programmatic macro system which allows the compiler to be extended by user code. Macros can be used to define syntactic constructs which would require primitives or built-in support in other languages. (http://clojure.org/macros)

The example you provide has specifically that requirement ("evaluate code in a postfix fashion") which is why a macro is the correct choice.

Upvotes: 1

Thumbnail
Thumbnail

Reputation: 13473

Your macro is better than your function: a macro is better than a function employing eval.

However, the function need not employ eval. You could write it

(defn reverse-fn [data] 
  (apply (last data) (butlast data)))

Then, for example, as before,

(reverse-fn [3 inc])
=> 4

On a related topic, you might find this explanation of transducers interesting.


Edit:

Notice that functions are literals, in the sense that a function evaluates to itself:

((eval +) 1 1)
=>  2

Upvotes: 0

user448810
user448810

Reputation: 17876

Simple rules are the best: Use a function if you can, a macro if you must.

Upvotes: 0

Frank C.
Frank C.

Reputation: 8088

In general:

Macros have their use however; macros expand at the point they are encountered so you will have one or more code blocks being inlined in the resulting byte code. They are ideal for encapsulating High Order DSL terminologies.

Functions, on the other hand, are wonderful for reuse where you localize the purity of the function and it can be called from multiple other functions without increasing the code footprint. They are ideal for localized or global usage.

REPL behavior consideration: A single function is easier to rework without worrying about fully evaluating the entire source file(s) to ensure all macro reference expansions get updated.

Hope this helps

Upvotes: 0

Related Questions