
---------- Forwarded message ---------- From: szergling <senatorzergling@gmail.com> Date: Wed, Feb 25, 2009 at 11:51 PM Subject: Re: More interesting stuff To: Marco Antoniotti <marcoxa@gmail.com> Cc: clazy-devel@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@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!
participants (1)
-
szergling