On 5 October 2011 11:38, Michael Jaaka <michael.ja...@googlemail.com> wrote:
> In fact I still don't know the macros works, I don't know when
> and how is evaluated and how symbols are evaluated.

Macros are just functions manipulating list structure (the data
structures produced by the reader when reading in Clojure code).

E.g. say we want to write our own 'and macro (which differs from
clojure.core/and in that it returns true if all xs evaluate to truthy
values at run time (rather than the last x)):

(defmacro my-and [& xs]
  (if (seq xs)
    `(let [x# ~(first xs)]
       (if x# (my-and ~@(next xs)) x#))
    true))

Actually, to make it clearer what's happening, we could also write it like so:

(defmacro my-and [& xs]
  (let [xsym (gensym "x")]
    (if (seq xs)
      (list `let [xsym (first xs)]
        (list `if xsym (list* `my-and (next xs)) xsym))
      true)))

Now if you write (my-and 1 2 false 3) in your code, the compiler will
notice that this is a list whose first symbol names a macro function
and call the macro function:

(apply #'my-and nil nil '(1 2 false 3))
; => (clojure.core/let [x12 1] (if x12 (user/my-and 2 false 3) x12))

(Don't worry about the nils -- they're needed because macro functions
in Clojure expect to receive extra magic arguments beyond those
explicitly named in their signatures. These are very useful
occasionally, but for the sake of simplicity, let's just forget about
them -- they're not used by my-and anyway.)

The return value is just another list; as it happens, clojure.core/let
is a macro too, so it too gets expanded. Only when the result of macro
expansion is no longer itself a macro call is JVM bytecode actually
emitted by the compiler.

Now for a more complex example:

(def x false)

(my-and 1 2 x 3)

We would expect this to return false at run time, and indeed it does,
but the macro function itself doesn't care about that -- it just
returns something like

(let [x1 1] (if x1 (my-and 2 x 3) x1))

Eventually the inner call gets expanded, so the code looks like

(let [x1 1] (if x1 (let [x2 2] (if x2 (my-and x 3) x2)) x1))

Ultimately we obtain

(let [x1 1]
  (if x1
    (let [x2 2]
      (if x2
        (let [x3 x] ; <- notice the x
          (if x3
            (let [x4 3]
              (if x4
                true
                x4))
            x3))
        x2))
     x1))

The 'x here is still just a symbol; the value of the Var it names only
becomes relevant at run time.

In fact, evaluation of arguments passed to macros only ever happens at
run time (well, there's the possibility of calling eval from inside a
macro to force a "recursive compilation" followed by evaluation of a
form; this is likely to be useful only in fairly esoteric cases). If
you want to prevent if from happening even then, you can wrap the
arguments in a 'quote form in the expansion:

(defmacro list-of-unevaled [& xs]
  (list* `list (map #(list 'quote %) xs)))

(list-of-unevaled (+ 1 2) (/ 5 0))
; => ((+ 1 2) (/ 5 0))

Hope this helps,
Michał

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to