chicken-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Chicken-users] possible lazy-let macro?


From: Daishi Kato
Subject: Re: [Chicken-users] possible lazy-let macro?
Date: Thu, 17 Nov 2005 21:17:54 +0900
User-agent: Wanderlust/2.15.1 (Almost Unreal) Emacs/21.4 Mule/5.0 (SAKAKI)

Hi!

Nice to know of identifier-syntax.
It looks useful once you understand how it works.
I also found it is faster than delay/force,
the functions are different though.

Well, nonetheless, there still are some overheads,
assigning variables, and cheking them if they are new.
What I want is no overhead, so that I can basically
replace all let with lazy-let, unless there is a side-effect.

Here is my solution, I hope someone review it.

(use srfi-1)

(define-macro (lazy-let vars . body)
  (let loop ([names (map car vars)] [xpr (cons 'begin body)])
    (cond
     ((list? xpr)
      (let* ([flat (map flatten xpr)]
             [cnt (lambda (x)
                    (length (filter (lambda (y) (memq x y)) flat)))])
        (let-values ([(nms1 nms2)
                      (partition (lambda (x) (> (cnt x) 1)) names)])
          (if (null? nms1)
              (map (lambda (x) (loop names x)) xpr)
              `(let ,(filter (lambda (x) (memq (car x) nms1)) vars)
                 ,(map (lambda (x) (loop nms2 x)) xpr))))))
     ((and (symbol? xpr) (memq xpr names))
      (cadr (assq xpr vars)))
     (else
      xpr))))

Note:
- not tested much
- not sure if there are any restrictions
- macro expanding may be costly, could be improved?

Daishi


At Wed, 16 Nov 2005 20:11:15 -0600,
Alex Shinn wrote:
> 
> At Wed, 16 Nov 2005 18:18:00 +0900, Daishi Kato wrote:
> > 
> > Does anyone know of any existence of a lazy-let macro,
> > which does the following?
> > 
> > <convert from>
> >
> [...]
> > 
> > (lazy-let ([a (get-a)][b (get-b)])
> >   (if (condition)
> >       (begin (display a) a)
> >       (begin (display b) b)))
> > 
> > <into>
> > 
> > (if (condition)
> >     (let ([a (get-a)]) (begin (display a) a))
> >     (let ([b (get-b)]) (begin (display b) b)))
> 
> This is sort of thing identifier-syntax was made for.  Basically,
> 
> (require-extension syntax-case)
> 
> (define-syntax foo (identifier-syntax bar))
> 
> replaces the symbol foo everywhere with bar (which can be any
> expression).  There's also a form
> 
> (identifier-syntax (id1 tmpl1) ((set! id2 e2) tmpl2))
> 
> which let's you specify what happens when the variable is set (id1 and
> id2 are dummy symbols for the sake of pattern matching).  Thus the
> example given in the manual shows how to conceptually treat a symbol
> as the car of a list:
> 
> (let ([x (list 0)])
>   (define-syntax car-x
>     (identifier-syntax
>       (id (car x))
>       ((set! id e) (set-car! x e))))
>   (let ([before car-x])
>     (set! car-x 1)
>     (list before car-x x)))
> => (0 1 (1))
> 
> Replacing a with (get-a) everywhere is thus trivial, but to only
> compute (get-a) once we need to check if it has already been computed.
> The implementation below stores the result in a temp variable (which
> is also used to allow setting a):
> 
> ;; using define-macro since syntax-case is so ugly
> (define-macro (lazy-let params . body)
>   (append (list 'lazy-let-with-temp-ids)
>           (list (map (lambda (x) (append x (list (gensym)))) params))
>           body))
> 
> ;; same lazy-let but of the form (lazy-let ((var val tmp) ...) . body)
> ;; so that we have a unique temp-var per var to store the values
> (define-syntax lazy-let-with-temp-ids
>   (syntax-rules ()
>     ((lazy-let ((var val tmp) ...) . body)
>      (let ((undef (list 'undef)))
>        (let ((tmp undef) ...)
>          (let-syntax
>              ((var (identifier-syntax
>                     (id (begin
>                           (if (eq? tmp undef)
>                             (set! tmp val))
>                           tmp))
>                     ((set! id e) (set! tmp e))))
>               ...)
>            . body))))))
> 
> ;; some definitions to use the example as-is:
> (define (condition) (zero? (random 2)))
> (define (get-a) (print "getting a") 'a-val)
> (define (get-b) (print "getting b") 'b-val)
> 
> ;; works as expected:
> #;7> (lazy-let ([a (get-a)][b (get-b)])
>        (if (condition)
>            (begin (print a) a)
>            (begin (print b) b)))
> getting a
> a-val
> a-val
> 
> ;; we can also set the lazy variables
> #;8> (lazy-let ([a (get-a)][b (get-b)])
>        (if (condition)
>          (begin (print a) (set! a 'new-a-val) a)
>          (begin (print b) (set! b 'new-b-val) b)))
> getting a
> a-val
> new-a-val
> 
> 
> -- 
> Alex




reply via email to

[Prev in Thread] Current Thread [Next in Thread]