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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With