From senatorzergling at gmail.com Sun Mar 8 09:40:05 2009 From: senatorzergling at gmail.com (szergling) Date: Sun, 8 Mar 2009 22:40:05 +1300 Subject: [clazy-devel] Fwd: More interesting stuff In-Reply-To: <8a4b88390902252141t641ad891rb0c415a4131cdea8@mail.gmail.com> References: <8a4b88390811061931q5d5bc59cp2eef52d1f40fa88b@mail.gmail.com> <5706a0e40902182005u2fc5abfdm633daef03ac7f870@mail.gmail.com> <8a4b88390902250251j2b3b25c2p2261aa4ffd2caf40@mail.gmail.com> <5706a0e40902250842w1ef70c80qdee82d5f77c03637@mail.gmail.com> <8a4b88390902252141t641ad891rb0c415a4131cdea8@mail.gmail.com> Message-ID: <8a4b88390903080140h4ae1c2dcj961e89f358d08323@mail.gmail.com> ---------- Forwarded message ---------- From: szergling Date: Thu, Feb 26, 2009 at 6:41 PM Subject: Re: More interesting stuff To: Marco Antoniotti [[...]] After thinking more, I still regard let as being different from letrec (saying this as a Lisper, Haskeller might very well have different expectations). I have a better example to show my confusion. Assuming full laziness, I expect the following -- (let ((x 1)) (let ((x (cons 1 x))) x)) => (1 . 1) (let ((x 1)) (letrec ((x (cons 1 x))) x)) => (1 1 1 1 1 ...) That's why I think we need to be able to differentiate between let and letrec. > Finally, I took some decisions w.r.t. your suggestions. > I made two block-like blocks, called LAZILY (and SLACKING: I kind of like > this one) in lieu of WITH-LAZINESS. So now you can do > (lazily (print 42) (cons (loop) 42)) > and CONS *will* be called lazily. You code-walking solution was the right > one. Code-walking is hard... my example was only a hack. It's completely buggy at the moment, but even if it works, it will fail on "confusing names" in "confusing positions", for example, (let ((car 2)) ...) or (lambda (car ...) ...) Might have to bring in Arnesi... From senatorzergling at gmail.com Sun Mar 8 09:23:52 2009 From: senatorzergling at gmail.com (szergling) Date: Sun, 8 Mar 2009 22:23:52 +1300 Subject: [clazy-devel] Fwd: More interesting stuff In-Reply-To: <8a4b88390902250251j2b3b25c2p2261aa4ffd2caf40@mail.gmail.com> References: <8a4b88390811061931q5d5bc59cp2eef52d1f40fa88b@mail.gmail.com> <5706a0e40902182005u2fc5abfdm633daef03ac7f870@mail.gmail.com> <8a4b88390902250251j2b3b25c2p2261aa4ffd2caf40@mail.gmail.com> Message-ID: <8a4b88390903080123o24d38156g8a53324b8d3bc41f@mail.gmail.com> ---------- Forwarded message ---------- From: szergling Date: Wed, Feb 25, 2009 at 11:51 PM Subject: Re: More interesting stuff To: Marco Antoniotti Cc: clazy-devel at 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 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) > , at 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! >