Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the benefit of defining the "or" operation in clojure as a macro rather than simply as a function?

Tags:

clojure

I'm hoping the explanation will give me better insight into the advantages of using macros.

like image 637
user1797455 Avatar asked Feb 05 '23 06:02

user1797455


1 Answers

In a function, all arguments are evaluated before its invocation.

This means that or as a function cannot be lazy, whereas a macro can rewrite or into an if statement that only evaluates branches when it's necessary to do so.


A bit more concretely, consider:

(or (cached-lookup) (expensive-operation))

...what it gets rewritten into looks like:

(let [or__1234__auto (cached-lookup)]
  (if or__1234__auto
    or__1234__auto
    (expensive-operation)))

...such that we only evaluate (expensive-operation) if the return value of (cached-lookup) is nil or false. You couldn't do that with a function while implementing regular JVM calling conventions: expensive-operation would always be evaluated, whether or not its result is needed, so that its result could be passed as an argument to the function.


Incidentally, you can implement a function in this case if you take zero-argument functions as your arguments. That is to say, you can do this:

(defn or*
  ([] false)                                                ; 0-arg case
  ([func-one] (func-one))                                   ; 1-arg case
  ([func-one func-two]                                      ; optimized two-arg case
   (let [first-result (func-one)]
     (if first-result
       first-result
       (func-two))))
  ([func-one func-two & rest]                               ; general case
   (let [first-result (func-one)]
     (if first-result
       first-result
       (apply or* func-two rest)))))

When you must implement a macro, it's often helpful to have it generate "thunks" (anonymous functions), and pass them to higher-order functions such as this one; this substantially aids composability, as a function can be wrapped, modified, or called using higher-level functions such as partial in ways a macro cannot.

like image 96
Charles Duffy Avatar answered Feb 14 '23 09:02

Charles Duffy