Skip to content
rvirding edited this page Sep 13, 2010 · 26 revisions

First macro

Create a file named hello.lfe and type in:

(defmodule hello
  (import (from io (format 1)(format 2)))
  (export (start 0)))

(defun start ()
  (macrolet ((f (args `(format ,@args))))
    (format '"Hello World~n")
    (f '"Hello Macro~n")))
$ erl -noshell -noinput -s lfe_boot start -pa ~/lfe/ebin
Erlang R13B01 (erts-5.7.2) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]

LFE Shell V5.7.2 (abort with ^G)
> (slurp '"hello.lfe")
#(ok hello)
> (: hello start)
Hello World
Hello Macro
ok
> (halt)

Debugging macros

There are two ways to debug macros in the current system.

The first is to use the to_exp option when compiling. This generates a source file with all macros expanded, just as it is passed into the compiler proper. So calling (c '"foo.lfe" '(to_exp)) will generate a file foo.expand with the expanded forms. This is a very heavy blunt instrument with very little control but it does show what the macros expand into.

The second is too use the macroexpand-1, macroexpand and macroexpand-all builtin shell functions (see lfe_shell.txt). You first slurp in the file containing the macros and then explicitly expand expressions using the macros. This gives better control but can be a bit tedious.

As far as I know these are the only ways of debugging macros, but someone else may have arrived at a better solution. :-)

Painful debugging

One strategy is to always aim for the simplest macro that will work.

Step 1: Simplest possible working macro.

(defmacro add100 (x) 
  100)

Step 2: Make sure the result is getting out with lfe_io:format.

(defun start ()
  (: lfe_io format '"~p~n" (list (add100 (+ 1 2))))

100

Step 3: Add the smallest possible code toward the goal.

(defmacro add100 (x)
  `(100 + ,x))            ; FAIL!

Step 4: If error, quote[’] the macro.

(defmacro add100 (x)
  `'(100 + ,x))

It will stop the evaluation at the quote, printing out:

(100 + (+ 1 2))

Step 5: Analyzing…

It should be (+ 100 (+ 1 2)) so I change the macro and remove the quote.

(defmacro add100 (x)
  `(+ 100 ,x))

103

Success!

Code walking macros

(defmodule codewalk
  (export (start 0)))

(eval-when-compile
  (defun replace
    ((_ _ '()) '())
    ((from to (head . tail)) (when (is_list head))
     (cons (replace from to head) 
           (replace from to tail)))
    ((from to (head . tail)) 
     (cons (if (== head from) to head) 
           (replace from to tail)))))

; Walks the syntax tree, replacing "self" with the function name.
(defmacro adefun
  ((fn . (args . es))
   (let ((new_es (replace 'self fn es)))
   `(defun ,fn ,args ,@new_es))))

; A function that call it self without using its function name.
(adefun countdown (x) 
        (if (== x 0) 
            '()
            (cons x (self (- x 1)))))

;> (countdown 10)
;(10 9 8 7 6 5 4 3 2 1)  

Codewalking change the syntax tree.

Y combinator

Ref: http://www.catonmat.net/blog/derivation-of-ycombinator/

(defun y (le)
  (apply (lambda (f) (apply f 
                            (list f)))
         (list (lambda (f)
                 (apply le 
                        (list (lambda (x) 
                                (apply (apply f 
                                              (list f))
                                       (list x)))))))))

; Calculate the length of a list.
(apply 
 (apply (fun y 1) 
        (list 
         (lambda (len)
           (lambda (li)
             (if (== li '())
               0
               (+ 1 (apply len (list  (tl li)))))))))
 (list '(a b c d)))

; 4

A Y combinator let anonymous function call itself. With the help of an argument, which is itself.