神刀安全网

Clojure Macros Tutorial – part 1: functions vs. macros #cljklipse @viebel

Clojure as any other LISP language is homoiconic : the code written in the language is encoded as data structures that the language has tools to manipulate.

The most powerful tool to manipulate code is the macro.

We all know how to call macros in our clojure code. But have you ever asked yourself:

What is exactly the difference between a macro and a function?

or:

What would it take to replace macro calls with function calls in a clojure program?

In this article, we will try to give an answer by stating two assertions and demonstrating them with live code inKLIPSE.

Clojure Macros Tutorial - part 1: functions vs. macros #cljklipse @viebel

Macros vs. functions

One common explanation of the difference between functions and macros is:

  • a function transforms values into other values.
  • a macro transforms code into other code.

We want to give a more precise answer, analysing two aspects of functions and macros:

  1. when the arguments are evaluated?
  2. is the return value evaluated?

Regarding to those two aspects, here are the differences between macros and functions:

arguments evaluation return value evaluated?
functions before function code execution not evaluated
macros only when macro code evaluates them explicitly evaluated

The proof for functions

About functions, we stated that:

  1. function arguments (the input) are evaluated before the function code execution
  2. function return value (the output) is not evaluated

Let’s check it by writing a function with different side effects in the input and the output. We’ll see what side effect is executed.

We will modify the background color of KLIPSE as side effects (with a simple function set-body-bg-color ): red for the input and green for the output.

As you can see below, the background is red: the input is evaluated and the output is not evaluated; the output is returned, but it’s not evaluated.

Q.E.D.∎

The proof for macros

About functions, we stated that:

  1. macros arguments (the input) are not evaluated before the macro code evaluates them explicitly
  2. macros return value (the output) is evaluated

Let’s check it by writing a macros with different side effects in the input and the output. The macro will not evaluate the input and we’ll see what side effect is executed.

As before, we will modify the background color of KLIPSE as side effects: red for the input and green for the output.

As you can see below, the background is green: the input is not evaluated and the output is evaluated; the output is returned, and evaluated.

Q.E.D.∎

If you wonder why we have to append $macros to the namespace and to reference the fully-qualified macro with self-hosted clojurescript , read Messing with Macros at the REPL .

Two assertions about functions and macros

Now we will try to see how functions and macros are interchangeable.

Let’s say we have a function foo-f and a macro foo-m with exactly the same code. Then, the following two assertions hold:

####Assertion #1: the macro expansion is equivalent to the function execution with arguments quoted

(= (macroexpand-1 '(foo-m arg1 arg2 ...))    (foo-f 'arg1 'arg2 '...)) 

####Assertion #2: the macro execution is equivalent to the evaluation of the function execution with arguments quoted

(= (foo-m arg1 arg2 ...)    (eval (foo-f 'arg1 'arg2 '...))) 

First assertion – macro expansion

The first assertion states that the macro expansion is equivalent to the function execution with arguments quoted.

(= (macroexpand-1 '(foo-m arg1 arg2 ...))    (foo-f 'arg1 'arg2 '...)) 

Let’s check it withKLIPSE, using the simple when macro as defined in core.clj :

Second assertion – function evaluation

The second assertion states that the macro execution is equivalent to the evaluation of the function execution with arguments quoted.

(= (foo-m arg1 arg2 ...)    (eval (foo-f 'arg1 'arg2 '...))) 

In order to check the second assertion, we have to define eval in clojurescript : this is easily done with self-host function eval-str :

(You can read more about self-host clojurescript in Self-Hosted Clojurescript in action – Part 1 .)

After having illustrated the differences between macros and functions, we will show step-by-step how to write the disp macro that we use so much in our blog (including in this article). Hopefully, it will illustrate and clarify all the basic building blocks of macros in clojure , namely:

  • regular quote
  • syntax quote
  • fully-qualified symbols
  • unquote
  • unique symbol generation
  • quote splicing

Follow us on twitter to get notified, when the next article in the macro tutorial series is published.

Clojure rocks!

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Clojure Macros Tutorial – part 1: functions vs. macros #cljklipse @viebel

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址