[Top][All Lists]

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

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

From: Alex Shinn
Subject: Re: [Chicken-users] possible lazy-let macro?
Date: Wed, 16 Nov 2005 20:11:15 -0600
User-agent: Wanderlust/2.10.1 (Watching The Wheels) SEMI/1.14.6 (Maruoka) FLIM/1.14.6 (Marutamachi) APEL/10.6 Emacs/21.3 (i386-pc-linux-gnu) MULE/5.0 (SAKAKI)

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
      (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))

;; 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) ...)
             ((var (identifier-syntax
                    (id (begin
                          (if (eq? tmp undef)
                            (set! tmp val))
                    ((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

;; 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


reply via email to

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