Lazy Let
Or, the Continuing Joy of Symbol Macros
A possibly harebrained use of symbol macros to produce variables that evaluate "lazily". A short one.
Check It Out
This is your REPL
;; Boring Binding
> (let* ((one (progn (print "binding one") 10))
(two (progn (print "biding two") (* one one)))
(three (progn (print "binding three") 1000)))
(if (zerop (random 2))
three
two))
"binding one"
"biding two"
"binding three"
1000
This is your REPL on lazy
:
;; Living Lazily
> (lazy ((two (progn (print "forcing two") (* one one)))
(one (progn (print "forcing one") 10))
(three (progn (print "forcing three") 1000)))
(if (zerop (random 2))
three
two))
"forcing two"
"forcing one"
100
Any questions?
A Macro
Today's hidden hero is humble symbol-macrolet
:
;; Folks, tell me all the ways this will fall on its lazy face.
(defmacro lazy ((&rest binding-forms) &body body)
(let ((lazy-control
(loop :for (name form) :in binding-forms
:collect (list name form (gensym "CACHED") (gensym "FORCED")))))
`(let ,(loop :for (n frm c f) :in lazy-control
:collect `(,c nil)
:collect `(,f nil))
(declare (ignorable ,@(loop for (nm fm c f) :in lazy-control
:collect c :collect f)))
(symbol-macrolet
,(loop :for (name form cached forced) :in lazy-control
:collect `(,name (if ,forced ,cached
(setf
,forced t
,cached ,form))))
,@body))))
That's all for today. Hope you enjoyed another half-baked Parenthetical Curio!