[comp.lang.scheme] Non-local variables

jk0@image.soe.clarkson.edu (Jason Coughlin) (05/09/90)

	Hi.  I've got a short little example which creates a non-local
variable to implement a simple little counter.  In my implementation of
Scheme, I create it like this:

(let ([a 0])
   (define counter
      (lambda ()
	   (set! a (+ a 1))
	   a
      )
   )
)

	PC-Scheme barfs on this complaining about an illegal letrec
syntax.  Both PC-Scheme and my Scheme allow the example in Dybvig's
book, _The Scheme Programming Language_:

(define counter
   (let ([a 0])
     (lambda ()
        (set! a (+ a 1))
        a
     )
   )
)
	Am I missing something important here?


-- 
Jason Coughlin ( jk0@sun.soe.clarkson.edu , jk0@clutx )
"Every jumbled pile of person has a thinking part that wonders what the
part that isn't thinking isn't thinking of." -- They Might Be Giants
"If you read the _TV Guide_, then there's no need for a TV." -- Lost Boys

krulwich@ils.nwu.edu (Bruce Krulwich) (05/10/90)

In article <1990May8.205719.2014@sun.soe.clarkson.edu>, jk0@image (Jason
Coughlin) writes:
>	Hi.  I've got a short little example which creates a non-local
>variable to implement a simple little counter.  In my implementation of
>Scheme, I create it like this:
>
>(let ([a 0])
>   (define counter
>      (lambda ()
>	   (set! a (+ a 1))
>	   a
>      )
>   )
>)
>
>  PC-Scheme barfs on this complaining about an illegal letrec syntax.

I think that the problem is that DEFINE's that are not at the top-level are
considered by Scheme (but not T or CL) to be local definitions like
LETREC's.  Probably the code you give above is transformed into a LET
containing a body-less LETREC, which is illegal.

This is an issue that has been discussed before.  Basically, the treatment
of internal DEFINE's as local definitions makes it impossible to have
static locally-scoped variables that span across procedures.  (Having
static variables for one procedure can be achieved as you showed later in
your message.)  Having internal DEFINE's result in local definitions
doesn't add any power, because they're identical in meaning to LETREC, but
to some people this seems more semantically correct than simply having all
DEFINE's result in top-level definitions.  Of course, another issue for
many people is that changing the meaning would make old code incorrect.


Bruce Krulwich
Institute for the Learning Sciences
krulwich@ils.nwu.edu

 

max@Neon.Stanford.EDU (Max Hailperin) (05/10/90)

In article <7478@accuvax.nwu.edu> krulwich@ils.nwu.edu (Bruce Krulwich) writes:
>In article <1990May8.205719.2014@sun.soe.clarkson.edu>, jk0@image (Jason
>Coughlin) writes: ...
>>(let ([a 0])
>>   (define counter
>>      (lambda ()
>>	   (set! a (+ a 1))
>>	   a
>>      )
>>   )
>> ...  PC-Scheme barfs on this complaining about an illegal letrec syntax.
>I think that the problem is that DEFINE's that are not at the top-level are
>considered by Scheme (but not T or CL) to be local definitions like
>LETREC's.  Probably the code you give above is transformed into a LET
>containing a body-less LETREC, which is illegal.

Yes, this is what's wrong. However, the syntax error is in fact discernable
without resorting to the explanation of the rewriting into a letrec (though
you wouldn't understand then why the error message mentions letrec).  The
syntax for let says it takes a "body" which is defined as zero or more
definitions followed by one or more expressions.  Hence, the code above
is illegal because there is no expression in the body of the let, just
a definition.

While I'm at it, I'd like to chip in my two cents on some of the other
issues raised:

>This is an issue that has been discussed before.  Basically, the treatment
>of internal DEFINE's as local definitions makes it impossible to have
>static locally-scoped variables that span across procedures. ...

This isn't true, though it requires what some consider an unaesthetic
idiom.  What you do is define the procedure names as some placeholder, e.g. 
#f, and then use set! within the appropriate lexical scope to mutate them
to name the actual procedures, made with lambda.  The main disadvantage
of this idiom, from my perspective, is that it makes the procedure names
mutable, which causes compilers and people alike to handle them gingerly.
On the other hand, I don't know of any better alternative -- see the below.

>Having internal DEFINE's result in local definitions
>doesn't add any power, because they're identical in meaning to LETREC,

Right, that's why R3RS makes them optional.  On the other hand, while
they don't add anything semantically, they can be nice syntactically
by helping prevent your code from gradually drifting off the right edge
of the page.

>but
>to some people this seems more semantically correct than simply having all
>DEFINE's result in top-level definitions.  Of course, another issue for
>many people is that changing the meaning would make old code incorrect.

The issue isn't "semantic correctness," but rather that we (yes, I am one
of the "some people") don't accept the premise that there is a unique
top-level.  Real systems frequently have levels of varying degrees of
top-ness, and it's hard to know just where the programmer intended a
definition to be installed, if not the current scope.  Aside from
this, the right level for a procedure might not be a top-level at
all, in any sense.  Take the case of two procedures which need to
share some private state and should themselves only be visible in
a restricted part of the program.  This works fine using the set!
approach, but can't be handled by the define-is-always-top-level
approach.

gintera@cpsc.ucalgary.ca (Andrew Ginter) (05/10/90)

In article <7478@accuvax.nwu.edu>, krulwich@ils.nwu.edu (Bruce Krulwich) writes:
> This is an issue that has been discussed before.  Basically, the treatment
> of internal DEFINE's as local definitions makes it impossible to have
> static locally-scoped variables that span across procedures.  (Having
> static variables for one procedure can be achieved as you showed later in
> your message.)

It seems to me that the variable "a" below is a locally scoped variable
which spans procedures...

(define dummy
  (let ((a 0))
    (list (lambda () (set! a (+ 1 a)) a)
          (lambda () (set! a (- 1 a)) a))))
(define count-up (car dummy))
(define count-down (cadr dummy))

Andrew Ginter, 403-282-2984, gintera@CPSC.UCALGARY.CA, Ginter@UNCAMULT.BITNET