pls explain set! scoping
Author Message
pls explain set! scoping

I'm learning Scheme and am not grokking the following (things I type are
preceded by a colon; replies from the interpreter, which are edited for
brevity, are preceded by nothing):

: (define t '(hi))

: (define setter1
(lambda (x)
(set! x '(ho))))

: (define setter2
(lambda (x)
(set-car! x 'ho)))

: (setter1 t)

: t
(hi)

: (setter2 t)

: t
(ho)

My questions are (1) why does setter2 succeed in "reaching" and changing t
but setter1 fails to do so? and (2) granted that this is how things work,
how can I, from within a lambda-expression as above, change t if t is not

--
matt neuburg, PhD

Wed, 07 Apr 1999 03:00:00 GMT
pls explain set! scoping

Quote:

>I'm learning Scheme and am not grokking the following (things I type are
>preceded by a colon; replies from the interpreter, which are edited for
>brevity, are preceded by nothing):
>: (define t '(hi))
>: (define setter1
>    (lambda (x)
>      (set! x '(ho))))
>: (define setter2
>    (lambda (x)
>      (set-car! x 'ho)))

[examples elided]

Quote:
>My questions are (1) why does setter2 succeed in "reaching" and changing t
>but setter1 fails to do so?

This is kind of an optical illusion. setter2 did _not_ change t.

The value of the global variable t is a pair. This pair never changes
throughout your example. What is _stored in_ the car of t is changed by
setter2, and so the _abstract_ value of t, considered as a list,
changes. The concrete value of t _as a pair_ is still the same pair of
slots in memory that it was all along.

Let's try some bad ASCII art:

t --is bound to--> |some location| --contains--  (the argument)
(global env)                               \      |
\     v
x --is bound to--> |some location| --contains--> |some pair|
(local env)                                  /         \
has as car   has as cdr
/             \
|the symbol "hi"|      ()

This is an _abstract_ picture, showing --relationships--> between
|objects| defined in the formal semantics of Scheme, although I've
probably made an awful hash of it somewhere. Don't think of the
arrows as pointers, because they aren't. Think of them as
relationships between concepts. Now, what does each of your examples
do to this diagram?

setter1 changes the "contains" arrow for x to point to a new place.
It does not touch the "contains" arrow for t.

setter2 changes the "has as car" arrow for the pair. Again, it does
NOT change the "contains" arrow for t. The value of t, considered as
a pair, never changes. However, the (abstract) value of t, considered
as a list, does change. This is what had you fooled into thinking you
could "touch" t.

Quote:
>                            and (2) granted that this is how things work,
>how can I, from within a lambda-expression as above, change t if t is not

As I said, you aren't really changing t in setter2. At least, not in
the sense you appear to mean.

By and large, and doubtless someone will catch me out on this, the
_only_ way to change one of those "contains" arrows is to use set! on
the variable you want to change. And the only way to get at a variable,
rather than a value, is to use its name in an appropriate scope.

There is no way in Scheme, as far as I know, to pass a variable,
rather than a value, as an argument _to a function_. You could write
a _macro_ that expands into an appropriate set! (not for beginners),
but in general, if you need something like this, you might do better
to think this stuff over and try another approach.

If, on the other hand, you're just playing around, please, _keep at it_.
Sooner or later it's bound to make some sort of sense.

Hope this helps,
Sean Case

---------------------------------------------

Code is an illusion. Only assertions are real.

Thu, 08 Apr 1999 03:00:00 GMT
pls explain set! scoping

You may find it helpful to take a look at some lecture notes that
Matthias Felleisen and I put together, especially on state:

http://www.cs.rice.edu/~shriram/311/Lectures/7.html
Assignment and Mutability

http://www.cs.rice.edu/~shriram/311/Lectures/11.html
Modeling State through Allocation

On the other hand, you might not, seeing as these are part of a
_series_ of lectures.  However, they're all on-line:

http://www.cs.rice.edu/~shriram/311/
Principles of Programming Languages

Enjoy!

'shriram

Thu, 08 Apr 1999 03:00:00 GMT
pls explain set! scoping

: I'm learning Scheme and am not grokking the following (things I type are
: preceded by a colon; replies from the interpreter, which are edited for
: brevity, are preceded by nothing):

: --
You will probably receive some textbook replies involving names
cells, bindings of names to cells, values, environments that map
cells to values, etc.

Instead, here is what typical implementations do:
(set! t '(hi))

There is an global object of type symbol named 't'.
It has a value field that points to the list '(hi).

(setter1 t)

The lambda variable x is bound to the value of t.
So the value of x is the list '(hi)
'x' is usually a name for a register or stack location.

(set! x '(ho))

Change the value of x to a pointer to the list '(ho).
Nothing setter1 does can alter the object 't', since only 't's value,

Scheme and Lisp pass values of expressions, not references to variables.
People get confused because almost all the values are internally
represented as pointers.

setter2 uses set-car! to clobber the internal structure of a list.
Since t and the lambda variable x both refer to this structure, the
values of both are affected.

Thu, 08 Apr 1999 03:00:00 GMT
pls explain set! scoping

+---------------
| There is no way in Scheme, as far as I know, to pass a variable,
| rather than a value, as an argument _to a function_.
+---------------

Well, what you *can* do is pass a *procedure* as a parameter. This can
give you a kind of object-oriented metaphor. For example, let's make a
"class" of settable/fetchable objects, then instantiate one of them, and
then change it:

> (define make-mutable-thing
(lambda (initial-value)
(let ((hidden-thing initial-value))
(lambda (op . arg)
(case op
((fetch) hidden-thing)
((set!) (set! hidden-thing (car arg)))
(else
(error "wrong operation on mutable-thing:" op arg)))))))
Then:

> (define foo (make-mutable-thing 47))
> (foo 'fetch)
47
> (foo 'set! 53)
> (foo 'fetch)
53
> (foo 'bletch 34 53)
wrong operation on mutable-thing: bletch (34 53)
>

And such "objects" (procedures) can then be passed (by "value") to other
procedures that invoke the getting & setting operations deep inside them:

> (foo 'fetch)
53
> (define mutable-thing-incrementor!
(lambda (x)
(let ((old (x 'fetch)))
(x 'set! (+ old 1)))))
> (mutable-thing-incrementor! foo)
> (mutable-thing-incrementor! foo)
> (foo 'fetch)
55
>

Note that two instances of a "mutable-thing" are distinct:

> (define a (make-mutable-thing 5))
> (define b (make-mutable-thing 17))
> (a 'fetch)
5
> (b 'fetch)
17
> (mutable-thing-incrementor! b)
> (b 'fetch)
18
> (a 'fetch)
5
>

The reason I call this sort of a "class" is that you can put more hidden
variables in the top-level "let" and more branches in the "case" and get
much more complicated "objects" and behavior. See any Scheme text for more...

-Rob

p.s.
Some Schemes also provide explicit "boxed variables", which are kind
of like a pair with no cdr, and special operations "unbox" and "set-box!"
analogous to "car" and "set-car!" on pairs.

-----

Silicon Graphics, Inc.          http://reality.sgi.com/rpw3/
2011 N. Shoreline Blvd.         Phone: 415-933-1673  FAX: 415-933-0979
Mountain View, CA  94043        PP-ASEL-IA

Sat, 10 Apr 1999 03:00:00 GMT
pls explain set! scoping

Quote:

>Scheme and Lisp pass values of expressions, not references to variables.
>People get confused because almost all the values are internally
>represented as pointers.

People had better get *un*confused fast, because Java is *exactly* the
same.  Java passes everything by value, and for arrays and objects,
that means passing the *identity* of the array or object.  So if I have

static void inc(int i) {
i++;
}

as a method, then
int x;
x = 1;
...
inc(x);
passes a copy of the value of x to inc(), and x is left unchanged.
In the same way,

static void inca(int a[]) {
a[0]++;
}

as a method, called like

int[] x = new int[1];
x[0] = 2;
...
inca(x);

passes a copy of the value of x to inca(), and x is left unchanged,
but the array that x *points* to is changed.

--
Mixed Member Proportional---a *great* way to vote!
Richard A. O'Keefe; http://www.cs.rmit.edu.au/%7Eok; RMIT Comp.Sci.

Mon, 12 Apr 1999 03:00:00 GMT

 Page 1 of 1 [ 6 post ]

Relevant Pages