Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

confused in a macro definition

I want to implement the lazy stream in SICP section 3.5.1

Firstly, I defined this two functions

(defmacro delay (form)
  `(lambda () ,form))

(defun force (form)
  (when form
    (funcall form)))

When we called:

(force (delay '(+ 1 2)))
;;=> (+ 1 2)

(force (delay (+ 1 2)))
;;=> 3

So that's worked. Then I go on defining `stream-cons', but this time there seems to be two ways:

(defmacro stream-cons (a b)
  `(cons ,a ,(delay b)))

(defmacro stream-cons (a b)
  `(cons ,a (delay ,b)))

I don't think they are different, but I am wrong! The first edition, which is a wrong edition, when called:

(force (cdr (stream-cons 'a (progn (print "hello") 2))))
;;=> (PROGN (PRINT "hello") 2)

(macroexpand '(stream-cons 'a (progn (print "hello") 2)))
;;=> (CONS 'A #<CLOSURE (LAMBDA # :IN STREAM-CONS) {25ABB3A5}>)

and the second edition, which is right, when called:

(force (cdr (stream-cons 'a (progn (print "hello") 2))))
;; 
;; "hello" 
;; => 2

(macroexpand '(stream-cons 'a (progn (print "hello") 2)))
;;=> (CONS 'A (DELAY (PROGN (PRINT "hello") 2)))

Now, I am very confused. Who can kindly help me to make clear the different of the two? Thanks a lot!

My Environment: Windows 32bits, SBCL 1.1.4

like image 681
xiepan Avatar asked Mar 23 '23 19:03

xiepan


1 Answers

This is an important concept to understand about macros.

The thing is that ,(delay b) is evaluated at macroexpansion time, i.e. the lambda is created in place and is wrapped over the literal value passed to it which is the list of symbols. So you get a constant function that always returns the same value - a list that happens to be your code.

You can picture it like this:

,(delay '(progn (print "hello") 2)) => (lambda () '(progn (print "hello") 2))

In the second variant (delay ,b):

(delay ,'(progn (print "hello") 2)) => (delay (progn (print "hello") 2))

The catch here is that the arguments to macro calls are passed literally, without evaluation. So delay receives effectively a quoted list ('(progn (print "hello") 2)). What comma does is cancel the quote if they meet.

like image 117
Vsevolod Dyomkin Avatar answered Apr 20 '23 14:04

Vsevolod Dyomkin