Home > Archive > Scheme > October 2006 > dynamic scope and free variables
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
dynamic scope and free variables
|
|
| Marlene Miller 2006-10-07, 10:03 pm |
| Does "free variable" make sense in a language with dynamic scoping?
For lexical scoping: "A variable x occurs free in E iff there is some use of
x in E that is not bound by any declaration of x in E."
- - - - - - - - - -
Assume dynamic scoping...
(let ((a 1)) b) ; b occurs free
(let ((a 1)) (lambda () a)) ; a occurs free!
(let ((p (lambda () a))) ; a occurs bound
(let ((q (lambda ()
(let ((a 2)) (p)))))
(q)))
(lambda (x)
(let ((a 1))
(let ((p (lambda () a))) ; a occurs bound OR free
(if x (p) p))))
(((lambda (a)
(let ((p (lambda () a))) ; a occurs bound AND free
(if (p) p 0)))
#t))
| |
| Pascal Bourguignon 2006-10-08, 4:10 am |
| "Marlene Miller" <marlenemiller@worldnet.att.net> writes:
> Does "free variable" make sense in a language with dynamic scoping?
Yes. Perhaps it's not very useful, but it makes sense.
> For lexical scoping: "A variable x occurs free in E iff there is some use of
> x in E that is not bound by any declaration of x in E."
>
> - - - - - - - - - -
> Assume dynamic scoping...
>
> (let ((a 1)) b) ; b occurs free
>
> (let ((a 1)) (lambda () a)) ; a occurs free!
When?
> (let ((p (lambda () a))) ; a occurs bound
When?
The notion of free variable is defined lexically.
But you're right that in face of dynamic scoping, free variables are
resolved dynamically, not lexically.
(let ((a 1))
(declare (special a)) ; how we'd declare dynamic scope in Common Lisp
(lambda ()
(let ((b 2))
(declare (special b))
(list a b))))
Here, a is a free variable of the lambda expression.
b is not a free variable of the lambda expression.
However both variables are resolved dynamically, when (list a b)
is executed (if ever).
(let ((f
(let ((a 1))
(declare (special a))
(lambda ()
(let ((b 2))
(declare (special b))
(list a b))))
))
(let ((a 0))
(declare (special a))
(funcall f)))
--> (0 2)
> (let ((q (lambda ()
> (let ((a 2)) (p)))))
> (q)))
p is a free variable of the lambda expression.
> (lambda (x)
> (let ((a 1))
> (let ((p (lambda () a))) ; a occurs bound OR free
> (if x (p) p))))
a is a free variable of (lambda () a)
The same a is a bound variable of the outermost lambda expression.
> (((lambda (a)
> (let ((p (lambda () a))) ; a occurs bound AND free
> (if (p) p 0)))
> #t))
Same here.
WHEN (lambda () a) is first called, a, the variable that is free in
(lambda () a) is dynamically bound to the parameter passed to the
outermost lambda.
WHEN (lambda () a) is called thereafter, a, the variable that is still
free in (lambda () a) is not dynamically bound to anything and therefore
an unbound variable error occurs.
[129]> (funcall (funcall (lambda (a)
(declare (special a))
(let ((p (lambda () (declare (special a)) a)))
(if (funcall p) p 0)))
t))
*** - EVAL: variable A has no value
[130]> (defparameter a 'hi) ; this declares a dynamic global variable.
A
[131]> (funcall (funcall (lambda (a)
(declare (special a))
(let ((p (lambda () (declare (special a)) a)))
(if (funcall p) p 0)))
t))
HI
[132]>
The notion of free variable is totally orthogonal to the notion of scoping.
--
__Pascal Bourguignon__ http://www.informatimago.com/
| |
| Steve VanDevender 2006-10-08, 4:11 am |
| "Marlene Miller" <marlenemiller@worldnet.att.net> writes:
> Does "free variable" make sense in a language with dynamic scoping?
Yes.
> For lexical scoping: "A variable x occurs free in E iff there is some use of
> x in E that is not bound by any declaration of x in E."
The same definition applies whether scoping is lexical or dynamic.
> - - - - - - - - - -
> Assume dynamic scoping...
If you have a version of Emacs handy, Emacs Lisp uses dynamic scoping.
It's relatively easy to translate these into Emacs Lisp to see what they
do in a dynamically-scoped language. The main difference is that Emacs
Lisp has the traditional separation of symbol and function namespaces,
unlike Scheme, so if you create a closure doing something like
(setq b (lambda () a))
You can't just say "(b)" to evaluate the closure; you have to say
"(funcall b)".
The only other thing you'll have to know to understand the translated
code below is that "(setq a value)" in Emacs Lisp is equivalent (for our
purposes) to "(define a value)" in Scheme.
> (let ((a 1)) b) ; b occurs free
>
> (let ((a 1)) (lambda () a)) ; a occurs free!
a is free in (lambda () a). In a lexically-scoped language, the closure
(lambda () a) returned by that expression would always return 1 when
called since a is bound to 1 in the lexically-enclosing environment of
the "let". In a dynamically-scoped language, calling the closure
(lambda () a) could produce different values depending on the binding of
a (if any) in the caller or any of its callers at the time the closure
is evaluated.
> (let ((p (lambda () a))) ; a occurs bound
> (let ((q (lambda ()
> (let ((a 2)) (p)))))
> (q)))
This returns the value 2.
(let ((p (lambda () a)))
(let ((q (lambda ()
(let ((a 2)) (funcall p)))))
(funcall q)))
=> 2
> (lambda (x)
> (let ((a 1))
> (let ((p (lambda () a))) ; a occurs bound OR free
> (if x (p) p))))
This one's a little more interesting.
(setq foo
(lambda (x)
(let ((a 1))
(let ((p (lambda () a)))
(if x (funcall p) p)))))
=> (lambda (x) (let ((a 1)) (let ((p (lambda nil a))) (if x (funcall p) p))))
(funcall foo nil)
=> (lambda nil a)
(funcall foo t)
=> 1
> (((lambda (a)
> (let ((p (lambda () a))) ; a occurs bound AND free
> (if (p) p 0)))
> #t))
(funcall (funcall (lambda (a)
(let ((p (lambda () a))) ; a occurs bound AND free
(if (funcall p) p 0)))
t))
Error: "Symbol's value as variable is void: a"
This makes sense; if we look at the inner expression:
(funcall (lambda (a)
(let ((p (lambda () a))) ; a occurs bound AND free
(if (funcall p) p 0)))
t)
=> (lambda nil a)
then trying to evaluate that when a is unbound produces the error.
Dynamic scoping makes semantic analysis of a program much more difficult
since in general the bindings in all active calls have to be examined to
determine the binding of a variable at any given point; in a
lexically-scoped language bindings can be determined from the static
lexical structure of the program test.
--
Steve VanDevender "I ride the big iron" http://hexadecimal.uoregon.edu/
stevev@hexadecimal.uoregon.edu PGP keyprint 4AD7AF61F0B9DE87 522902969C0A7EE8
Little things break, circuitry burns / Time flies while my little world turns
Every day comes, every day goes / 100 years and nobody shows -- Happy Rhodes
| |
| Marlene Miller 2006-10-08, 7:04 pm |
| "Marlene Miller" wrote
> Does "free variable" make sense in a language with dynamic scoping?
>
> For lexical scoping: "A variable x occurs free in E iff there is some use
of
> x in E that is not bound by any declaration of x in E."
Thank you Pascal and Steve for your helpful insights and detailed
explanations for each example. Thank you for showing me how to use the
dynamic scoping feature of Common Lisp and Emacs Lisp.
To make make sense of "free variable", I tried /applying/ the definition to
a dynamic context:
In a language with dynamic scoping, to know whether a use of variable x in
expression E is bound by a declaration of x in E, we execute E. When we
evaluate x, we look for a binding of x in the environment. If there is no
binding, we conclude x is free. Do we have to actually execute E or is it
sufficient to (lexically) find a path of execution to x that has no binding
of x in the environment?
I created my examples to show a path of execution to 'a' that has no binding
of 'a' in the environment. In particular, when E returns a procedure whose
body is 'a', 'a' is free. I set up one example to conditionally call or
return a procedure and another example to both call and return a procedure.
These two examples told me "free variable" does not make sense.
Now I see that my /creative interpretation/ of "free variable" is
idiosyncratic.
- - - - - - - - - -
(define g
(lambda (p)
(let ((f (lambda (x) (p args))))
f)))
In a dynamically scoped language, does the scope of the declaration of 'x'
include all procedures in the program that could be bound to p and do not
declare 'x'?
| |
| Pascal Costanza 2006-10-08, 7:04 pm |
| Marlene Miller wrote:
>
> (define g
> (lambda (p)
> (let ((f (lambda (x) (p args))))
> f)))
>
> In a dynamically scoped language, does the scope of the declaration of 'x'
> include all procedures in the program that could be bound to p and do not
> declare 'x'?
>
Yes, a dynamically scoped variable effectively has global (or
indefinite) scope.
One possible reason why you may have trouble understanding dynamic
scoping is that the term is actually a misnomer. See Section 3 in
http://oopweb.com/LISP/Documents/cltl/VolumeFrames.html for a more
precise terminology.
Pascal
--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
| |
| Marcin 'Qrczak' Kowalczyk 2006-10-08, 7:04 pm |
| Pascal Costanza <pc@p-cos.net> writes:
> One possible reason why you may have trouble understanding dynamic
> scoping is that the term is actually a misnomer. See Section 3 in
> http://oopweb.com/LISP/Documents/cltl/VolumeFrames.html for a more
> precise terminology.
I disagree with Common Lisp design of dynamic scoping. Here is my
design.
The core language is lexically scoped. There are two kinds of
variables, among others: plain mutable variables ("plain variables"
for short) and dynamically scoped variables ("dynamic variables" for
short). Dynamic variables, although technically lexically scoped,
are used to implement dynamic scoping.
There are 3 fundamental operations related to plain variables:
A1. Making a new plain variable with an initial value.
A2. Getting the current value.
A3. Setting the new value.
and 4 operations related to dynamic variables:
B1. Making a new dynamic variable with an initial value.
B2. Getting the current value.
B3. Setting the new value.
B4. Local rebinding to a new cell with a new initial value.
Local rebinding means that code executed in the context of the
rebinding construct, when it refers to the variable, operates on
an independent mutable cell.
Informal "types" of operations ('->' is the type constructor of an
impure function):
1. () -> Variable(a)
2. Variable(a) -> a
3. Variable(a) * a -> ()
4. Variable(a) * a * (() -> b) -> b
If you don't ever use B4, dynamic variables behave exactly like plain
variables, i.e. dynamic variables are an extension of plain variables
by providing an additional operation. For this reason I see no point
in distinguishing A2 from B2, or A3 from B3: they have the same
semantics, they differ only by being applicable to variables with
a distinct additional capability.
Distinguishing the syntax of A1 and B1 is natural: somehow it must be
indicated what kind of variable is created.
Mutability is orthogonal to dynamic scoping. It makes sense to have a
kind of a variable which is like a plain variable but without A3, and
a kind of a variable which is like a dynamic variable but without B3.
They allow to express more constraints with a potential for optimization.
I won't consider them here.
So a natural syntax has forms for A1, B1, A2=B2, A3=B3, and B4.
Common Lisp does something weird: it uses the same syntax for A1 and B4,
where the meaning is distinguished by a special declaration. Here is
its syntax:
Directly named plain variables:
A1. (let ((name value)) body) and other forms
A2. name
A3. (setq name value), (setf name value)
First-class dynamic variables:
B1. (gensym)
B2. (symbol-value variable)
B3. (set variable value), (setf (symbol-value variable) value)
B4. (progv `(,variable) `(,value) body)
Directly named dynamic variables:
B1. (defvar name value), (defparameter name value)
B2. name
B3. (setq name value), (setf name value)
B4. (let ((name value)) body) and other forms
Dynamic variables in Lisp come in two flavors: first-class variables
and directly named variables. Directly named variables are always
global. You can convert a direct name to a first-class variable by
(quote name).
Plain variables have only the directly named flavor and they are
always local. You can emulate the first-class flavor by wrapping a
variable in a pair of closures or a closure with dual getting/setting
interface (needs a helper macro in order to be convenient). You can
emulate a global plain variable by wrapping a dynamic variable in a
symbol macro, ignoring its potential for local rebinding. You can
emulate creation of a new first-class variable by using a dynamic
variable and ignoring its potential for local rebinding, but this
can't be used to refer to an existing directly named plain variable.
In order to create a plain variable, you must be sure that its name
is not already used by a dynamic variable in the same scope.
So any essential functionality is possible to obtain, but the syntax
is irregular.
--
__("< Marcin Kowalczyk
\__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/
| |
| Pascal Bourguignon 2006-10-08, 7:04 pm |
| "Marlene Miller" <marlenemiller@worldnet.att.net> writes:
> "Marlene Miller" wrote
> of
>
> Thank you Pascal and Steve for your helpful insights and detailed
> explanations for each example. Thank you for showing me how to use the
> dynamic scoping feature of Common Lisp and Emacs Lisp.
>
> To make make sense of "free variable", I tried /applying/ the definition to
> a dynamic context:
>
> In a language with dynamic scoping, to know whether a use of variable x in
> expression E is bound by a declaration of x in E, we execute E. When we
> evaluate x, we look for a binding of x in the environment. If there is no
> binding, we conclude x is free. Do we have to actually execute E or is it
> sufficient to (lexically) find a path of execution to x that has no binding
> of x in the environment?
>
> I created my examples to show a path of execution to 'a' that has no binding
> of 'a' in the environment. In particular, when E returns a procedure whose
> body is 'a', 'a' is free. I set up one example to conditionally call or
> return a procedure and another example to both call and return a procedure.
> These two examples told me "free variable" does not make sense.
>
> Now I see that my /creative interpretation/ of "free variable" is
> idiosyncratic.
The notion of free variable, is purely "textual":
Assuming that expression is fully expanded down to lambda:
(define (free-variables expression bound-variables)
(cond
((symbol? expression) (if (member expression bound-variables)
'()
(list expression)))
((atom? expression) '())
((eqv? 'lambda (car expression))
(let ((bound-variables (append (cadr expression) bound-variables)))
(mapcan (lambda (subexpr) (free-variables subexpr bound-variables))
(cddr expression))))
(else (mapcan (lambda (subexpr) (free-variables subexpr bound-variables))
expression))))
(free-variables '((lambda (b) (+ ((lambda (x) (* 2 x)) a) b)) 3) '())
--> (+ * a)
(free-variables '(lambda () a) '())
--> (a)
Now with:
(define g
(lambda (p)
(let ((f (lambda (x) (p args))))
f)))
Let's expand the let in g:
(define g
(lambda (p)
((lambda (f) f)
(lambda (x) (p args)))))
(free-variables '(lambda (p) ((lambda (f) f) (lambda (x) (p args)))) '())
--> (args)
(free-variables '(lambda (x) (p args)) '())
--> (p args)
The difference, is that when you have lexical scoping, that inner
lambda is always in a scope where p is bound, but when you consider
dynamic scoping, the scope where that inner lambda may be evaluated
can change, and sometimes, p will be bound and sometimes it won't.
But in all cases, p is a free variable of (lambda (x) (p args)).
The notion of free variable allows the compiler to produce a closure
when lexical scoping is used, and to generate dynamic references when
dynamic scoping is used, for the free variables.
(*) Given:
(define (atom? x) (not (pair? x)))
(define (last x) (cond ((null? x) x) ((null? (cdr x)) x) (else (last (cdr x)))))
(define (nconc! a b) (if (null? a) b (begin (set-cdr! (last a) b) a)))
(define (mapcan fun list)
(if (null? list)
list
(nconc! (fun (car list)) (mapcan fun (cdr list)))))
--
__Pascal Bourguignon__ http://www.informatimago.com/
NOTE: The most fundamental particles in this product are held
together by a "gluing" force about which little is currently known
and whose adhesive power can therefore not be permanently
guaranteed.
| |
| Pascal Costanza 2006-10-08, 7:04 pm |
| Marcin 'Qrczak' Kowalczyk wrote:
> Pascal Costanza <pc@p-cos.net> writes:
>
>
> I disagree with Common Lisp design of dynamic scoping.
I haven't referred to CLtL2 to endorse its design of dynamic scoping.
There are surely better designs, like Scheme's parameter objects or
ISLISP's dynamic variables. I have referred to CLtL2 because of the
clearer terminology.
> Here is my design.
>
> The core language is lexically scoped. There are two kinds of
> variables, among others: plain mutable variables ("plain variables"
> for short) and dynamically scoped variables ("dynamic variables" for
> short). Dynamic variables, although technically lexically scoped,
> are used to implement dynamic scoping.
>
> There are 3 fundamental operations related to plain variables:
>
> A1. Making a new plain variable with an initial value.
> A2. Getting the current value.
> A3. Setting the new value.
>
> and 4 operations related to dynamic variables:
>
> B1. Making a new dynamic variable with an initial value.
> B2. Getting the current value.
> B3. Setting the new value.
> B4. Local rebinding to a new cell with a new initial value.
Like B4, there is also A4 for lexical variables. For example, local
rebindings of lexical variables become observable in some
hygiene-breaking macros.
> Local rebinding means that code executed in the context of the
> rebinding construct, when it refers to the variable, operates on
> an independent mutable cell.
[...]
> If you don't ever use B4, dynamic variables behave exactly like plain
> variables, i.e. dynamic variables are an extension of plain variables
> by providing an additional operation. For this reason I see no point
> in distinguishing A2 from B2, or A3 from B3: they have the same
> semantics, they differ only by being applicable to variables with
> a distinct additional capability.
If you don't distinguish A2 from B2, and A3 from B3, there can be
confusion wrt whether you are actually dealing with a lexical or a
dynamic variable in a given program fragment. That's why in Common Lisp,
it is important to stick to the naming convention for dynamically scoped
variables, i.e., they have to start and end in asterisks. This
effectively creates a new namespace, and it would have been better had
Common Lisp enforced a distinct namespace for dynamic variables. That's
essentially the advantage of Scheme's parameter objects or ISLISP's
dynamic variables: They are guaranteed to be in their own namespace.
> Distinguishing the syntax of A1 and B1 is natural: somehow it must be
> indicated what kind of variable is created.
Yup.
> Mutability is orthogonal to dynamic scoping. It makes sense to have a
> kind of a variable which is like a plain variable but without A3, and
> a kind of a variable which is like a dynamic variable but without B3.
> They allow to express more constraints with a potential for optimization.
Yup.
> Common Lisp does something weird: it uses the same syntax for A1 and B4,
> where the meaning is distinguished by a special declaration. Here is
> its syntax:
>
> Directly named plain variables:
> A1. (let ((name value)) body) and other forms
> A2. name
> A3. (setq name value), (setf name value)
>
> First-class dynamic variables:
> B1. (gensym)
It would be more appropriate to mention (make-symbol) here. (gensym)
only exists for a special use case in macro programming.
> B2. (symbol-value variable)
> B3. (set variable value), (setf (symbol-value variable) value)
The first variant is deprecated.
> B4. (progv `(,variable) `(,value) body)
The backquote makes this look more complicated than it actually is. So
the improved version is this:
B1. (make-symbol "name")
B2. (symbol-value variable)
B3. (setf (symbol-value variable) value)
B4. (progv (list variable) (list value) body ...)
It would be nice if there were a "listless" variant of progv, though.
> Directly named dynamic variables:
> B1. (defvar name value), (defparameter name value)
> B2. name
> B3. (setq name value), (setf name value)
> B4. (let ((name value)) body) and other forms
>
> Dynamic variables in Lisp come in two flavors: first-class variables
> and directly named variables. Directly named variables are always
> global.
Not always, but most of the time. You can create local dynamic variables
with the local special declaration. But that's indeed unusual. See
http://www.tfeb.org/lisp/hax.html#DYNAMIC-STATE for a use case, though.
Something like this is also straightforward to achieve with parameter
objects.
> You can convert a direct name to a first-class variable by
> (quote name).
You can also convert a first-class variable to a direct name by
unquoting, for example in a macro expansion. (You also have to take care
that the name has a "special" declaration that applies in the given
context.)
> Plain variables have only the directly named flavor and they are
> always local. You can emulate the first-class flavor by wrapping a
> variable in a pair of closures or a closure with dual getting/setting
> interface (needs a helper macro in order to be convenient). You can
> emulate a global plain variable by wrapping a dynamic variable in a
> symbol macro, ignoring its potential for local rebinding. You can
> emulate creation of a new first-class variable by using a dynamic
> variable and ignoring its potential for local rebinding, but this
> can't be used to refer to an existing directly named plain variable.
All correct.
> In order to create a plain variable, you must be sure that its name
> is not already used by a dynamic variable in the same scope.
....which is the reason for the naming convention. For first-class
dynamic variables (according to your terminology) created with
make-symbol or the likes, this is irrelevant.
> So any essential functionality is possible to obtain, but the syntax
> is irregular.
The syntax is good enough for the typical use cases. For more exotic
uses, it's indeed a good idea to build nicer abstractions on top of what
Common Lisp provides in rather crude ways.
The advantage in Common Lisp is that the semantics of all the constructs
are specified, including those of the first-class variants, so you can
rely on them and write portable programs. In all multi-threaded
implementations of CL that I am aware of, the semantics of dynamic
variables are also mostly sane.
Pascal
--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
| |
| Marcin 'Qrczak' Kowalczyk 2006-10-09, 4:12 am |
| Pascal Costanza <pc@p-cos.net> writes:
> Like B4, there is also A4 for lexical variables.
What do you mean? There is not: there is no way to call a given
function such that references to a given variable from the function
refer to a separate mutable cell (and at the same time other threads
see the original cell).
> If you don't distinguish A2 from B2, and A3 from B3, there can be
> confusion wrt whether you are actually dealing with a lexical or a
> dynamic variable in a given program fragment.
It doesn't matter because the semantics of these operations is the
same when applied to both variables. Anyway it can be seen which kind
of variable is meant by looking at its definition.
> That's why in Common Lisp, it is important to stick to the naming
> convention for dynamically scoped variables,
No, the Common Lisp naming convention is there because being dynamic
is not a property of a variable but of a symbol. This means that you
can't shadow a dynamic variable with a lexical variable. And that
it's not sufficient to look at the definition (e.g. LET) to see
which variable is meant, but you must search for separate SPECIAL
declarations.
>
> Not always, but most of the time. You can create local dynamic
> variables with the local special declaration.
It doesn't create a new dynamic variable but enables access to a
global dynamic variable (associated with the symbol).
> The advantage in Common Lisp is that the semantics of all the
> constructs are specified,
It's not specified what happens when you SETQ an undefined variable.
--
__("< Marcin Kowalczyk
\__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/
| |
| Steve VanDevender 2006-10-09, 4:12 am |
| "Marlene Miller" <marlenemiller@worldnet.att.net> writes:
> "Marlene Miller" wrote
> of
[color=darkred]
> To make make sense of "free variable", I tried /applying/ the definition to
> a dynamic context:
>
> In a language with dynamic scoping, to know whether a use of variable x in
> expression E is bound by a declaration of x in E, we execute E. When we
> evaluate x, we look for a binding of x in the environment. If there is no
> binding, we conclude x is free. Do we have to actually execute E or is it
> sufficient to (lexically) find a path of execution to x that has no binding
> of x in the environment?
If there is a declaration of x in an expression E, you don't have to
execute E to figure out what x is bound to in the scope of E; the
declaration tells you. The difference between static and dynamic
scoping is meaningful only for free variables in an expression.
The most critical difference is that if an expression E1 returns some
subexpression E2 such as a closure that contains variable references
that are free in E2, in a staticly-scoped language it's possible to
determine what E2 evaluates to by looking at only the written definition
of E1 (and the global environment in effect at the time E1 was defined).
In a dynamicly-scoped language, what E2 evaluates to depends on the
bindings of those free variables in the runtime environment, and is not
necessarily predictable from the bindings in effect at the time E1 was
defined.
--
Steve VanDevender "I ride the big iron" http://hexadecimal.uoregon.edu/
stevev@hexadecimal.uoregon.edu PGP keyprint 4AD7AF61F0B9DE87 522902969C0A7EE8
Little things break, circuitry burns / Time flies while my little world turns
Every day comes, every day goes / 100 years and nobody shows -- Happy Rhodes
| |
| Pascal Costanza 2006-10-09, 4:12 am |
| Marcin 'Qrczak' Kowalczyk wrote:
> Pascal Costanza <pc@p-cos.net> writes:
>
>
> What do you mean? There is not: there is no way to call a given
> function such that references to a given variable from the function
> refer to a separate mutable cell (and at the same time other threads
> see the original cell).
Of course:
(let ((x 42))
(call-with-different-x
(lambda () ... x ...)))
What x refers to depends on whether and how call-with-different-x binds x.
>
> It doesn't matter because the semantics of these operations is the
> same when applied to both variables. Anyway it can be seen which kind
> of variable is meant by looking at its definition.
See above.
>
> No, the Common Lisp naming convention is there because being dynamic
> is not a property of a variable but of a symbol. This means that you
> can't shadow a dynamic variable with a lexical variable.
Yes, you can.
(let ((x 42))
(declare (special x))
(let ((x 4711))
... here, x is lexical ...))
See also 3.1.2.1.1.4 in the HyperSpec.
Only if a variable is globally proclaimed to be special, it cannot be
shadowed with a lexical one anymore.
> And that
> it's not sufficient to look at the definition (e.g. LET) to see
> which variable is meant, but you must search for separate SPECIAL
> declarations.
Yep.
>
> It doesn't create a new dynamic variable but enables access to a
> global dynamic variable (associated with the symbol).
(let ((x 42))
(declare (special x))
; if x wasn't dynamically bound before
; then this is a local dynamic binding
))
>
> It's not specified what happens when you SETQ an undefined variable.
That's incorrect. It's specified that this is an error. (See 3.1.2.1.1
in the HyperSpec.)
Pascal
--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
| |
| Marcin 'Qrczak' Kowalczyk 2006-10-09, 4:12 am |
| Pascal Costanza <pc@p-cos.net> writes:
>
> Of course:
>
> (let ((x 42))
> (call-with-different-x
> (lambda () ... x ...)))
It influences what function to call, it doesn't alter the environment
of a previously existing function - it happens at compile time. It's
completely different from rebinding a dynamic variable.
>
> Yes, you can.
>
> (let ((x 42))
> (declare (special x))
> (let ((x 4711))
> ... here, x is lexical ...))
Ok, only if each dynamic variable is separately declared as special in
each scope it is used. Silly syntax. Why can't a name automatically
refer to the same dynamic variable in all scopes where it's not
shadowed to a different variable? It would not need a naming
convention.
In my design the core language has lexical scoping. Dynamic variables
are expressed in a lexically scoped world.
I also disagree with the common Lisp choice of letting each thread
start with global bindings of special variables. In my design threads
inherit the bindings from the place of thread creation.
>
> (let ((x 42))
> (declare (special x))
> ; if x wasn't dynamically bound before
> ; then this is a local dynamic binding
> ))
But it's a binding of the same dynamic variable as other dynamic
variables called x. It doesn't create a dynamic variable, it only
creates a cell of an existing dynamic variable.
[1]> (defun f () (declare (special x)) x)
F
[2]> (let ((x 42)) (declare (special x)) (f))
42
>
> That's incorrect. It's specified that this is an error. (See 3.1.2.1.1
> in the HyperSpec.)
"Non-constant variables can be assigned by using setq or bound[3] by using let."
I don't see an error; it's left unspecified.
In CLISP it doesn't signal an error:
[3]> (setq asdasdf 4)
4
--
__("< Marcin Kowalczyk
\__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/
| |
| Pascal Costanza 2006-10-09, 4:12 am |
| [We are probably boring the Schemers to death here, but anyway... ;)]
Marcin 'Qrczak' Kowalczyk wrote:
> Pascal Costanza <pc@p-cos.net> writes:
>
>
> It influences what function to call, it doesn't alter the environment
> of a previously existing function - it happens at compile time. It's
> completely different from rebinding a dynamic variable.
Ah, that's what you mean. Thanks for the clarification.
>
> Ok, only if each dynamic variable is separately declared as special in
> each scope it is used. Silly syntax. Why can't a name automatically
> refer to the same dynamic variable in all scopes where it's not
> shadowed to a different variable? It would not need a naming
> convention.
I don't care so much about the syntax, because I can change it. ;)
> In my design the core language has lexical scoping. Dynamic variables
> are expressed in a lexically scoped world.
>
> I also disagree with the common Lisp choice of letting each thread
> start with global bindings of special variables.
ANSI Common Lisp doesn't say anything in this regard. It's indeed the
case that different implementations treat this case differently.
> In my design threads
> inherit the bindings from the place of thread creation.
I agree that that's a good idea.
>
> But it's a binding of the same dynamic variable as other dynamic
> variables called x. It doesn't create a dynamic variable, it only
> creates a cell of an existing dynamic variable.
OK, I think that's nitpicking (also what I said).
>
> "Non-constant variables can be assigned by using setq or bound[3] by using let."
>
> I don't see an error; it's left unspecified.
Ah, you're right. My mistake.
Pascal
--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
| |
| Marcin 'Qrczak' Kowalczyk 2006-10-09, 8:06 am |
| Pascal Costanza <pc@p-cos.net> writes:
>
> ANSI Common Lisp doesn't say anything in this regard. It's indeed the
> case that different implementations treat this case differently.
Oops, it was lower case "common", and the pun with Common Lisp was not
intended :-)
--
__("< Marcin Kowalczyk
\__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/
| |
|
|
| Marlene Miller 2006-10-12, 10:02 pm |
| > The notion of free variable, is purely "textual":
>
> Assuming that expression is fully expanded down to lambda:
>
> (define (free-variables expression bound-variables)
> (cond
> ((symbol? expression) (if (member expression bound-variables)
> '()
> (list expression)))
> ((atom? expression) '())
> ((eqv? 'lambda (car expression))
> (let ((bound-variables (append (cadr expression) bound-variables)))
> (mapcan (lambda (subexpr) (free-variables subexpr
bound-variables))
> (cddr expression))))
> (else (mapcan (lambda (subexpr) (free-variables subexpr
bound-variables))
> expression))))
>
It looks like x is a "free variable" if it appears outside of any (lambda
(x) e), ignoring dynamic scope rules. Then p is not free is this expression.
(lambda (p)
(lambda (x) (p args)))
Also, the variable reference p does not refer to the declaration of p, but
that is another matter. I think I see my mistake. When talking about scope,
we don't say p is bound. We say p /refers/ to a binding.
| |
| Marlene Miller 2006-10-12, 10:02 pm |
| > One possible reason why you may have trouble understanding dynamic
> scoping is that the term is actually a misnomer. See Section 3 in
> http://oopweb.com/LISP/Documents/cltl/VolumeFrames.html for a more
> precise terminology.
Thank you for the reference. This definition makes sense to me.
EoPL seems to associate static scope with evaluating the body of a procedure
in the extension of the environment of the closure, and dynamic scope with
evaluating the body of a procedure in the extension of the current
environment. I began thinking static and dynamic scope actually meant which
environment was extended. While I was searching for the meaning of dynamic
scope, I found some lecture notes which expressed static and dynamic scope
using equations of denotational semantics. As far as I could tell [guess],
the equations make the same association. Am I on the right track?
http://www.cs.cornell.edu/Courses/c.../lecture_17.pdf
http://www.cs.cornell.edu/Courses/c.../lec10-fa06.pdf
| |
| Pascal Costanza 2006-10-12, 10:02 pm |
| Marlene Miller wrote:
>
> Thank you for the reference. This definition makes sense to me.
>
> EoPL seems to associate static scope with evaluating the body of a procedure
> in the extension of the environment of the closure, and dynamic scope with
> evaluating the body of a procedure in the extension of the current
> environment. I began thinking static and dynamic scope actually meant which
> environment was extended. While I was searching for the meaning of dynamic
> scope, I found some lecture notes which expressed static and dynamic scope
> using equations of denotational semantics. As far as I could tell [guess],
> the equations make the same association. Am I on the right track?
>
> http://www.cs.cornell.edu/Courses/c.../lecture_17.pdf
> http://www.cs.cornell.edu/Courses/c.../lec10-fa06.pdf
I only briefly looked at the material, but this looks good to me. The
explanations for dynamic scoping there are pretty standard.
The claim that dynamic scoping defeats modularity is misleading, though.
See the Section "Dynamic Scoping as a State-Decomposition Discipline" in
"The Art of the Interpreter" by Sussman/Steele for a different take on this:
http://library.readscheme.org/servl...the+interpreter
Pascal
--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
|
|
|
|
|