---------- Forwarded message ----------
From: szergling <senatorzergling(a)gmail.com>
Date: Wed, Feb 25, 2009 at 11:51 PM
Subject: Re: More interesting stuff
To: Marco Antoniotti <marcoxa(a)gmail.com>
Cc: clazy-devel(a)common-lisp.net
On Thu, Feb 19, 2009 at 5:05 PM, Marco Antoniotti wrote:
> only recently I did some hacking again.
Good to hear back from you. I wasn't sure if the clazy mailing lists
are working. I did forward my previous email to the list, but it
hasn't gone through. I'll try and forward this to the list again.
>
> I was dabbling with the idea of removing the LAZY:CALLS as well but
> maybe through a reader macro, although I loathe doing so, as then you
> must hijack either [] or {}. In your examples
Yeah, I'm not too keen on that either. On the one hand, the
code-walker example would allow laziness to be specified once only for
all sub-forms. On the other hand, with reader macros, we get
fine-grained control, allowing us to switch between eager & laziness
(hopefully seamlessly, and without too much confusion? -- is this
good?).
> (letrec/lazy ((x {lcons 1 {lcons 1 {list+ x {lcdr x}}}}))
> (list-force n x))
>
> But I am not so sure about it. Also, I would just change your
> letrec/lazy to let/lazy (my initial implementation is a little silly).
letrec was basically Scheme inspired. Perhaps under laziness, let is
the same as letrec? I haven't though too much about this, so I don't
really know...
(let ((x 1))
(let ((x x))
x))
(let ((x 1))
(letrec ((x x))
x))
I would expect different behaviour from each alternative under eager
evaluation, but under laziness, I'm not so sure...
I guess the value of (letrec ((x x)) x) under eager evaluation could
be undefined? As an example, under the only Scheme I have handy at the
moment, Elk,
> (letrec ((x x)) x)
()
Although I think it should be undefined...
> Finally, I know it may be a little far fetched, but you can actually
> be more devious :)
>
> (def-lazy-function cons (x y) (cons (delay x) (delay y))
> (def-lazy-function car (x) (force (car x)))
> (def-lazy-function cdr (x) (force (cdr x)))
> ... ect ...
>
> def-lazy-function (unlike deflazy) does not define a strict version of
> the function, thus it does not redefine regular strict functions. Now
> your code can be written as
>
> (letrec/lazy ((x {cons 1 {cons 1 {list+ x {cdr x}}}}))
> (list-force n x))
The laziness becomes even more invisible if we can slap on a
with-laziness very very far outside this form, assuming it is
embedded/nest deeply amongs other sexps:
(let ((x (cons 1 (cons 1 (list+ x (cdr x))))))
...)
I guess it effectively makes a lazy Lisp...
Yeah, I basically cannot decide on what form of lazy call syntax looks
best. Perhaps this
decision should be delayed further?
clazy is a nice twist on laziness, I'll have to find more time to play
with it someday. Thanks!
> On Fri, Nov 7, 2008 at 4:31 AM, szergling <senatorzergling(a)gmail.com> wrote:
>
> Hi again,
>
> I have just hacked up something that I thought was cool. It began with
> this observation:
>
> We can form "infinite" lists using recursive definitions. Here's how
> one can make an infinite list of 1's. First some utility functions:
>
> (deflazy lcons (x y)
> (cons (delay x)
> (delay y)))
>
> (deflazy lcar (cons)
> (force (car cons)))
>
> (deflazy lcdr (cons)
> (force (cdr cons)))
>
> (deflazy list-times (n list)
> (if (null list)
> '()
> (call 'lcons
> (* n (call 'lcar list))
> (call 'list-times n
> (call 'lcdr list)))))
>
> (deflazy list+ (list1 list2)
> (if (null list1)
> '()
> (call 'lcons
> (+ (call 'lcar list1)
> (call 'lcar list2))
> (call 'list+
> (call 'lcdr list1)
> (call 'lcdr list2)))))
>
> ;; Force and collect the first n items.
> (defun list-force (n list)
> (loop
> with l = list
> for i below n
> for first-time-p = t then nil
> while l
>
> unless first-time-p
> do (setf l (lcdr l))
>
> collect (lcar l)))
>
> ;; --------------------
>
> An infinite list of 1's.
>
> (let ((x nil))
> (flet ((get-x () x)
> ((setf get-x) (val)
> (setf x val)))
> (symbol-macrolet ((x (get-x)))
> (setf x
> (call 'lcons 1 x))
> x)))
>
> Codify the pattern above:
>
> ;; (put 'letrec/lazy 'common-lisp-indent-function 1)
>
> (defmacro letrec/lazy (bindings &body body)
> (let* ((bindings (mapcar (lambda (x)
> (if (and (list x)
> (= 2 (length x)))
> x
> (list x nil)))
> bindings))
> (vars (mapcar #'car bindings))
> (val-forms (mapcar #'cadr bindings))
> (gvars (mapcar (lambda (x)
> (gensym (symbol-name x)))
> vars))
> (fun-names (mapcar (lambda (x)
> (gensym (format nil "GET-~a" x)))
> vars)))
> (with-unique-names (value)
> (flet ((make-getter (var fun-name)
> `(,fun-name () ,var))
> (make-setter (var fun-name)
> `((setf ,fun-name) (,value)
> (setf ,var ,value)))
> (make-symbol-macro (var fun-name)
> `(,var (,fun-name)))
> (make-initialiser (var val-form)
> `(setf ,var ,val-form)))
> `(let ,vars
> (flet (,@(mapcar #'make-getter vars fun-names)
> ,@(mapcar #'make-setter vars fun-names))
> (symbol-macrolet ,(mapcar #'make-symbol-macro
> vars fun-names)
> ,@(mapcar #'make-initialiser vars val-forms)
> ,@body)))))))
>
> Some examples:
>
> (letrec/lazy ((x (call 'lcons 1 x)))
> (list-force 10 x))
> (1 1 1 1 1 1 1 1 1 1)
>
> ;; List elements keep doubling.
> (letrec/lazy ((x (call 'lcons 1 (call 'list-times 2 x))))
> (list-force 10 x))
> (1 2 4 8 16 32 64 128 256 512)
>
> ;; Fibonacci. Just like in those Haskell examples.
> (letrec/lazy ((x (call 'lcons 1
> (call 'lcons 1
> (call 'list+
> x (call 'lcdr x))))))
> (list-force 20 x))
> (1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765)
>
> I think we will have to remove explicit "calls" by using a code
> walker. In the meantime, here's a quick hack to demonstrate what the
> code might look like without all those calls.
>
> (defmacro with-laziness ((&rest options) &body body)
> (declare (ignore options))
> (labels ((transform (form)
> (if (atom form)
> form
> (destructuring-bind (name . args)
> form
> (if (lazy-function-name-p name)
> `(call ',name ,@(mapcar #'transform args))
> form)))))
> (cons 'progn
> (mapcar #'transform body))))
>
> (letrec/lazy ((x (with-laziness ()
> (lcons 1 (lcons 1 (list+ x (lcdr x)))))))
> (list-force 20 x))
>
> With a proper code-walker, with-laziness can wrap the whole form like
> this:
>
> (with-laziness ()
> (letrec/lazy ((x (lcons 1 (lcons 1 (list+ x (lcdr x))))))
> (list-force 20 x)))
>
> Don't you think this is cool?
>
> Unfortunately, there's a risk of infinite recursive/loops with this type
> of construct.
>
> I have to admit (see eg the list-times and list+ functions) that
> I've still not completely "assimilated" the evaluation rules in this
> new delayed DSL subset of Common Lisp, but what's there is enough for
> me to test things out. Nice work!
>