Page 1 of 1

Macros

Posted: Fri Dec 28, 2012 11:42 am
by omarasl
Hello everybody ,
can someone help me in macros .
are there any useful books or sites which contains good examples of macros .
Actually , I have some functions about sorted list and binary trees, I have to write the same functions by using macros .
I don`t know how , and my functions are not short (about 9 functions) .
so if there is someone can help me , I will put the code to discuss about it .
Thank you very much .

Re: Macros

Posted: Fri Dec 28, 2012 12:20 pm
by nuntius
Which dialect are you using? Scheme and CL have significantly different macro systems...

The following sources describe CL macros.

Practical Common Lisp has a couple introductory chapters

On Lisp has much deeper coverage

Re: Macros

Posted: Fri Dec 28, 2012 4:10 pm
by omarasl
thanks for your reply , actually my question about common lisp macros

Re: Macros

Posted: Fri Dec 28, 2012 8:28 pm
by sylwester
Well. Common Lisp macroes are easy when the problem is easy.
A little example here:

Code: Select all

;; function
(defun add10 (num)
   (+ 10 num))
Now a macro i CL is a function with the source arguments as parameters and the result is what the CL actually evaluates.
It means you would make a macro with the body evaluating to the list structure (+ 10 <whatever you put as num>) like this:

Code: Select all

;; old style, but more verbose and less easy to read (when macro is more complex)
(defmacro add10 (num)
  (list '+ 10 num))

;; or by using quasiquote
(defmacro add10 (num)
  `(+ 10 ,num))
So when you write (add10 (+ 3 5)) add10 is expanded with (+ 3 5) as num and the result is (+ 10 (+ 3 5)).
Being so simple it does not have problems with variable capturing (hygiene), arguments being evaluated more than once and sometimes in the wrong order, but for real life situations those things are what makes CL macros difficult to master. When testing macros you should always try arguments with side effects like (add10 (print 10)) that should print 10 once and evaluate to 20. With more arguments they should print in order once each before returning whatever it should. Use this to see what your macro does and what gets feed to CL:

Code: Select all

(macroexpand-1 '(add10 (print 10))) 
==> (+ 10 (PRINT 10))
Have fun :)

Re: Macros

Posted: Sat Dec 29, 2012 6:23 am
by omarasl
thanks for replying
as I understand ( maybe I understood wrong) that we can replace the function to macro by simple changes in code , is it right?????
for example if I have this function which use to insert in Binary search tree :


(defun BST-insert (B E)
(if (BST-empty-p B)
(make-bin-tree-leaf E)
(BST-nonempty-insert B E)))

how I can make macro instead of this function (with the same internal code ) ?????????

Re: Macros

Posted: Sat Dec 29, 2012 7:51 am
by Goheeca
For better understanding
if you have a macro:

Code: Select all

(defmacro add10-m (num)
  `(+ 10 ,num))
you can also have a function with quasiquotation:

Code: Select all

(defun add10-f (num)
  `(+ 10 ,num))
The difference is that macros are evaluated in compile-time and get unevaluated arguments. Back to the aforedefined function, the function can be called like this:

Code: Select all

(add10-f '(+ 1 2))
or more clearly:

Code: Select all

(add10-f (list '+ 1 2))
and it returns:

Code: Select all

(+ 10 (+ 1 2))
But we give it only data and get data from it too, coincidentally they look like code (on the input side we reached it with quote -- that apostrophe). If we use a macro instead of a function for example:

Code: Select all

(add10-m (+ 1 2))
The compiler invokes the macro function and passes the calling form* to that function (it's nicely parsed, due to the lambda-list in a defmacro form) so we don't quote, it does the compiler.
The macro function returns something which is expected to be code which is processed by the compiler further and afterwards the result of compilation is evaluated during run-time.
The macro can be written with the help of our function:

Code: Select all

(defmacro add10-m (num)
  (add10-f num))
Maybe will help you the information: in CL beside macros exist reader macros which take part during read-time, the key is to realise differences among run-time, compile-time and read-time. At least for me it was the key, but reader macros are beyond this single post.
Rather try this sample:

Code: Select all

(defmacro some-value () (read))
(defun add (num) (+ num (some-value))
(add 10)
After typing defun form (insert the code into REPL line by line) it will hang because of read so type some lisp form which evaluates to number and try the new defined function; it won't hang because there is no read in that function.

*Compare function lambda-list and macro one (for example &whole keyword).

Re: Macros

Posted: Sat Dec 29, 2012 8:29 am
by sylwester
omarasl wrote:thanks for replying
as I understand ( maybe I understood wrong) that we can replace the function to macro by simple changes in code , is it right?????
for example if I have this function which use to insert in Binary search tree :


(defun BST-insert (B E)
(if (BST-empty-p B)
(make-bin-tree-leaf E)
(BST-nonempty-insert B E)))

how I can make macro instead of this function (with the same internal code ) ?????????
Change the function so that when run it will return like this:

Code: Select all

(defun BST-insert (B E) ...)

(BTS-insert 'q 'w) ==>
(if (BST-empty-p q)
      (make-bin-tree-leaf w)
    (BST-nonempty-insert q w))
Then change from defun to defmacro. It's not perfect since B might be (make-bin-tree-leaf e) so you should make sure every argument is used exactly once in the resulting code. E is used twice but in different branches so is ok, but B is used twice in one workflow and should be rewritten:

Code: Select all

(BTS-insert '(make-bin-tree-leaf e) 'w) ==>
(let ((tmpvar  (make-bin-tree-leaf e)))
  (if (BST-empty-p tmpvar)
      (make-bin-tree-leaf w)
    (BST-nonempty-insert tmpvar w)))
Then you should use gensym in the macro to make tmpvar a unique symbol for each run.

Re: Macros

Posted: Sat Dec 29, 2012 9:41 am
by Goheeca
And after you get hygiene and a multiple evaluation issue you can stop reinventing the wheel and use well-known macros: with-gensyms & once-only.

Re: Macros

Posted: Sat Dec 29, 2012 4:41 pm
by sylwester
Goheeca wrote:And after you get hygiene and a multiple evaluation issue you can stop reinventing the wheel and use well-known macros: with-gensyms & once-only.
Useful, but learning Lisp is all about reinventing the wheel 8-)