Re: lazy-let & loop

Bruno Haible <[email protected]>
Newsgroups gmane.lisp.clisp.devel
Message-ID <3298330.GXKRH2WazP@omega>
Hi Sam,

> I wonder if something like "lazy-binding" could help: bind the variable
> but not initialize its value. until it is explicitly set, accessing it
> reaches up in the environment. This would require something like
> `lazy-let` with the following test case:
> 
> --8<---------------cut here---------------start------------->8---
> (let ((a 1))
>   (lazy-let (a b)
>     (print a) ; prints 1
>     (print b) ; error - unbound variable
>     (setq a 10) ; inner binding of a is activated
>     (print a) ; prints 10
>     (setq b 22)) ; binding of b is activated
>   (print b) ; error - unbound variable
>   (print a)) ; prints 1
> --8<---------------cut here---------------end--------------->8---
> 
> while it is easy to avoid `lazy-let` above by putting `let` after the
> second `print`, it is not so easy with `loop` because we need the
> binding to exist around the main `tagbody` but to do initialization in
> the preamble inside `tagbody`.

Your proposal introduces a new approach to scoping: Have the scope depend
on whether the access to a variable is a read or a write access.

While this may provide a small advantage in implementing LOOP, I believe
that it is a really bad idea in general.

1) The purpose of the scopes in the language definition is that by looking
   a the (fully macroexpanded) source code of a function, the user can
   tell to which binding a certain variable reference refers.
   If the user furthermore has to _scan_ the source code for assignments,
   this lookup is significantly more complicated.

2) What about conditional accesses?
     (lazy-let (a)
       (if (> (random) 0.5)
         (setq a 10))
       (lambda (a) (print a)))

3) There are forms which combine a read access and a write access, like
   (INCF b).

Instead, maybe we should provide explicit control of the environment in
which a form will be evaluated/compiled. For example:

  (let ((a 1))
    (environment-let env-with-a
      (let ((b 2))
        (in-enviroment env-with-a (list a b)))))

will evaluate (list a b) in an environment that contains the binding of a
but not the binding of b. This would be equivalent to

  (let ((a 1))
    (let ((#:g1 (lambda () (list a b))))
      (let ((b 2))
        (funcall #:g1))))

Such a clisp specific set of special-operators ENVIRONMENT-LET and
IN-ENVIRONMENT could be emulated for other implementations through a
code walker.

Still, I don't know whether such a thing is really needed for the LOOP
macro, where we have a finite set of initforms and bindings.

> The problem with loop is that under certain circumstances we separate
> initializing and binding of a variable.

This is not a problem by itself. It is explicitly allowed by CLHS 6.1.1.4
"Implementations can interleave the setting of initial values with the bindings."

What I see as a problem is that users are not warned when they write
unportable code. That is, we should have a warning (in the spirit of
"gcc -fsanitize=undefined" for C) whenever the interpretation of an initform
actually depends on the environment.

Bruno


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
clisp-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/clisp-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.