Definitions at run-time
Ivan Tomac — 2005-03-10 07:10:44
For some strange reason some of the recent posts to this message board made me dig
through the archives and re-read some of the old posts as well as go through most of the
papers on Joy again.
I've noticed one thing that I haven't seen earlier that just doesn't seem to add up. On the
"Further Frequently Asked Questions about Joy" page
(
http://www.latrobe.edu.au/philosophy/phimvt/joy/faq1.html) Manfred talks about the
reasons why having defining words avaliable at run-time is a bad idea. The reason he
states in the end is one relating to creation of variables that act the same way as they
would in imperative languages.
I have one question and one (obvious) observation:
1) Wouldn't this only be a problem with dynamically scoped languages? If Joy was lexically
scoped this wouldn't be an issue.
2) The imperative notion of variables can be simulated with files as well. Furthermore the
whole concept of binding names to values and functions is analogous to concepts of
filesystems and databases.
Ivan
William Tanksley, Jr — 2005-03-13 17:22:24
Ivan Tomac <
e1_t@...> wrote:
> For some strange reason some of the recent posts to this message board made
> me dig through the archives and re-read some of the old posts as well as go
> through most of the papers on Joy again.
Hey, I did that too :-). It's almost worth being proven wrong to see
the traffic on the list pick up :-).
> (http://www.latrobe.edu.au/philosophy/phimvt/joy/faq1.html) Manfred talks about the
> reasons why having defining words avaliable at run-time is a bad idea. The
> reason he states in the end is one relating to creation of variables that act the
> same way as they would in imperative languages.
That's not quite what he says -- he says that such definitions would
not act in a way he considers appropriate to functional languages.
The reason that functional languages don't allow mutable variables is
because the variables in lambdas aren't mutable; thus, the languages
modify what they can do in order to more closely conform to their
models. Definitions in Joy are similar to variables in lambda
expressions.
This is a reasonable choice, but not the only one.
> I have one question and one (obvious) observation:
> 1) Wouldn't this only be a problem with dynamically scoped languages? If Joy
> was lexically scoped this wouldn't be an issue.
Er... True, but not for the reason you're stating. A lexically scoped
language cannot possibly have defining words available at runtime, so
no issue. Lexical scope means that definition availability is
determined only by the static source text. If defining words were
available at runtime, we'd have dynamic scoping.
With that said, there's nothing bad about dynamic scope and allowing
redefinitions.
> Ivan
-Billy
Ivan Tomac — 2005-03-14 04:34:55
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr"
<wtanksleyjr@g...> wrote:
> > (http://www.latrobe.edu.au/philosophy/phimvt/joy/faq1.html)
Manfred talks about the
> > reasons why having defining words avaliable at run-time is a bad
idea. The
> > reason he states in the end is one relating to creation of
variables that act the
> > same way as they would in imperative languages.
>
> That's not quite what he says -- he says that such definitions would
> not act in a way he considers appropriate to functional languages.
>
> The reason that functional languages don't allow mutable variables
is
> because the variables in lambdas aren't mutable; thus, the languages
> modify what they can do in order to more closely conform to their
> models. Definitions in Joy are similar to variables in lambda
> expressions.
>
How is that different from what I've just said? You've simply worded
it better. Imperative variables = mutable variables.
What I'm saying is that it is possible for a language to not allow
mutable variables and at the same time provide defining words that
execute at run-time.
> This is a reasonable choice, but not the only one.
>
> > I have one question and one (obvious) observation:
> > 1) Wouldn't this only be a problem with dynamically scoped
languages? If Joy
> > was lexically scoped this wouldn't be an issue.
>
> Er... True, but not for the reason you're stating. A lexically
scoped
> language cannot possibly have defining words available at runtime,
so
> no issue. Lexical scope means that definition availability is
> determined only by the static source text. If defining words were
> available at runtime, we'd have dynamic scoping.
>
> With that said, there's nothing bad about dynamic scope and allowing
> redefinitions.
>
Alright. Consider a lexically scoped concatenative language.
Imagine entering something like this at the prompt (using Manfred's
notation from the FAQ):
[a 3] DEF2
[b a] DEF2
[a [a 4] DEF2 a] i a b
and on stack you end up with 3 3 4 3.
The re-binding occured at run-time and yet the language is lexically
scoped.
> -Billy
Ivan
William Tanksley, Jr — 2005-03-14 05:54:47
Ivan Tomac <
e1_t@...> wrote:
>"William Tanksley, Jr" <wtanksleyjr@g...> wrote:
> > > Manfred talks about the
> > > reasons why having defining words avaliable at run-time is a bad
> idea. The
> > > reason he states in the end is one relating to creation of
> > > variables that act the
> > > same way as they would in imperative languages.
> > That's not quite what he says -- he says that such definitions would
> > not act in a way he considers appropriate to functional languages.
> How is that different from what I've just said? You've simply worded
> it better. Imperative variables = mutable variables.
Sorry I was unclear. My intended point was that he didn't say that
dynamic definitions were a _bad_ idea; rather, he explained why he
didn't personally choose to do it.
> What I'm saying is that it is possible for a language to not allow
> mutable variables and at the same time provide defining words that
> execute at run-time.
No, I'd disagree -- for the reasons he listed. Now, I don't have any
argument with a language providing both or neither; but the fact is
that if you allow dynamic redefinition you've got mutable variables.
> > Er... True, but not for the reason you're stating. A lexically
> > scoped
> > language cannot possibly have defining words available at runtime,
> > so
> > no issue. Lexical scope means that definition availability is
> > determined only by the static source text. If defining words were
> > available at runtime, we'd have dynamic scoping.
> Alright. Consider a lexically scoped concatenative language.
> Imagine entering something like this at the prompt (using Manfred's
> notation from the FAQ):
> [a 3] DEF2
> [b a] DEF2
> [a [a 4] DEF2 a] i a b
> and on stack you end up with 3 3 4 3.
> The re-binding occured at run-time and yet the language is lexically
> scoped.
If the definitions are dynamic (at runtime) the language is dynamicly
scoped. In your above example, if the scoping were truly lexical, the
third DEF2 would apply to the entire quotation in which it appeared
(including the initial appearance of a), since that's its lexical
context.
> Ivan
-Billy
Ivan Tomac — 2005-03-14 13:43:25
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr" <wtanksleyjr@g...> wrote:
> > What I'm saying is that it is possible for a language to not allow
> > mutable variables and at the same time provide defining words that
> > execute at run-time.
>
> No, I'd disagree -- for the reasons he listed. Now, I don't have any
> argument with a language providing both or neither; but the fact is
> that if you allow dynamic redefinition you've got mutable variables.
>
How? In the examples I provided there are no mutable variables. In the last example 'a'
gets rebound at run-time.
> > Alright. Consider a lexically scoped concatenative language.
> > Imagine entering something like this at the prompt (using Manfred's
> > notation from the FAQ):
>
> > [a 3] DEF2
> > [b a] DEF2
> > [a [a 4] DEF2 a] i a b
>
> > and on stack you end up with 3 3 4 3.
> > The re-binding occured at run-time and yet the language is lexically
> > scoped.
>
> If the definitions are dynamic (at runtime) the language is dynamicly
> scoped. In your above example, if the scoping were truly lexical, the
> third DEF2 would apply to the entire quotation in which it appeared
> (including the initial appearance of a), since that's its lexical
> context.
>
I don't think so. I'm not good at wording these things (it seems every time I try I get stuck
in a pointless argument until after about 10 or so posts I finally manage to explain what I
meant) so I looked up a definition of dynamic binding (surprisingly, finding a simple yet
consise definition of it took a bit longer then I thought it would).
To quote the definition:
"In a dynamically scoped language an identifier can be referred to, not only in the block
where it is declared, but also in any function or procedure called from within that block,
even if the called procedure is declared outside the block."
That is clearly not the case in my examples as a word defined prior to the definition of 'a'
would not be able to access it even if it is called within the quotation where 'a' has been
defined.
Now to look up a definition for lexical or static scoping:
"In a lexically scoped language, the scope of an identifier is fixed at compile time to some
region in the source code containing the identifier's declaration. This means that an
identifier is only accessible within that region (including procedures declared within it).
This contrasts with dynamic scope where the scope depends on the nesting of procedure
and function calls at run time.
Statically scoped languages differ as to whether the scope is limited to the smallest block
(including begin/end blocks) containing the identifier's declaration or to whole function
and procedure bodies, or some larger unit of code). The former is known as static nested
scope."
In my examples the region is the environment from the point where the actual word has
been defined - because DEF2 is available at run-time that means it's possible to quote it
and hence delay it's execution until the quotation has been executed/interpreted using the
i combinator. The scope of 'a' does not depend on the nesting of words and quotations at
run-time.
>
> -Billy
Ivan
William Tanksley, Jr — 2005-03-14 16:02:14
Ivan Tomac <
e1_t@...> wrote:
>"William Tanksley, Jr" <wtanksleyjr@g...> wrote:
> > > What I'm saying is that it is possible for a language to not allow
> > > mutable variables and at the same time provide defining words that
> > > execute at run-time.
> > No, I'd disagree -- for the reasons he listed. Now, I don't have any
> > argument with a language providing both or neither; but the fact is
> > that if you allow dynamic redefinition you've got mutable variables.
> How? In the examples I provided there are no mutable variables. In the last example 'a'
> gets rebound at run-time.
And the effect is almost exactly the same as a mutable variable --
right? The only difference is that already-compiled code will see the
old value (or will it, I'm not sure how Joy handles redefinition?).
Functional languages have a reason for not allowing mutable variables:
they destroy the ability to staticly analyse lambda expressions (the
formal term is that they destroy "referential transparency"). This
reason applies in this case.
Now, as I've said before, I don't believe that this reason is very
compelling for a concatenative language; but it's nonetheless a
reason.
> > > Alright. Consider a lexically scoped concatenative language.
> > > Imagine entering something like this at the prompt (using Manfred's
> > > notation from the FAQ):
> > > [a 3] DEF2
> > > [b a] DEF2
> > > [a [a 4] DEF2 a] i a b
> > > and on stack you end up with 3 3 4 3.
> > > The re-binding occured at run-time and yet the language is lexically
> > > scoped.
> > If the definitions are dynamic (at runtime) the language is dynamicly
> > scoped. In your above example, if the scoping were truly lexical, the
> > third DEF2 would apply to the entire quotation in which it appeared
> > (including the initial appearance of a), since that's its lexical
> > context.
> I don't think so. I'm not good at wording these things (it seems every time I try I get
> stuck
> in a pointless argument until after about 10 or so posts I finally manage to explain what I
> meant) so I looked up a definition of dynamic binding (surprisingly, finding a simple yet
> consise definition of it took a bit longer then I thought it would).
> To quote the definition:
Interesting definition. I can use it.
> "In a dynamically scoped language an identifier can be referred to, not only in the block
> where it is declared, but also in any function or procedure called from within that block,
> even if the called procedure is declared outside the block."
> That is clearly not the case in my examples as a word defined prior to the definition of 'a'
> would not be able to access it even if it is called within the quotation where 'a' has been
> defined.
Why do you assume that a word defined prior to the definition of 'a'
wouldn't be able to access it? How could you possibly enforce that? If
the definition doesn't happen until runtime, how does your parser know
what variables have and haven't been defined? It really can't -- an
unrecognised word has to be assumed to be a variable that's been
defined earlier in the program's execution, since the parser can have
no knowledge of what the programmer could dynamicly define.
What I'm saying is that if your variable definitions are dynamic, your
scoping also must be dynamic.
> In my examples the region is the environment from the point where the actual word has
> been defined - because DEF2 is available at run-time that means it's possible to quote it
> and hence delay it's execution until the quotation has been executed/interpreted
> using the i combinator. The scope of 'a' does not depend on the nesting of words
> and quotations at run-time.
I don't understand the last sentance. It seems to contradict the rest
of what you've said.
> Ivan
-Billy
Ivan Tomac — 2005-03-15 08:24:28
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr"
<wtanksleyjr@g...> wrote:
> > How? In the examples I provided there are no mutable variables. In
the last example 'a'
> > gets rebound at run-time.
>
> And the effect is almost exactly the same as a mutable variable --
> right? The only difference is that already-compiled code will see
the
> old value (or will it, I'm not sure how Joy handles redefinition?).
No it isn't almost exactly the same as a mutable variable. A variable
is either mutable or it isn't mutable. In this case it isn't mutable -
you can't change the state, you can however bind a symbol to a new
value. In lambda terms it's a lot like let expressions.
The code that has already been compiled will see the old value and
that is the whole point - you can't change it's state, you can't make
it see new bindings.
How Joy handles redefinition is completely irrelevant. The whole point
of all this is that Joy is trying to separate defining words from all
other words even though it isn't necessary for what it's trying to
achieve.
> Functional languages have a reason for not allowing mutable
variables:
> they destroy the ability to staticly analyse lambda expressions (the
> formal term is that they destroy "referential transparency"). This
> reason applies in this case.
>
I agree. However these are not mutable variables.
> > "In a dynamically scoped language an identifier can be referred
to, not only in the block
> > where it is declared, but also in any function or procedure called
from within that block,
> > even if the called procedure is declared outside the block."
> > That is clearly not the case in my examples as a word defined
prior to the definition of 'a'
> > would not be able to access it even if it is called within the
quotation where 'a' has been
> > defined.
>
> Why do you assume that a word defined prior to the definition of 'a'
> wouldn't be able to access it? How could you possibly enforce that?
If
> the definition doesn't happen until runtime, how does your parser
know
> what variables have and haven't been defined? It really can't -- an
A word defined prior to the definition of 'a' would not have 'a' in
it's environment therefore it would not be able to access it.
Defining words are the only words that modify the envionment and
considering in this case they're no different from other words with
side-effects (such as IO functions - in fact defining new words is
really just a form of IO) they can only be executed at run-time.
Therefore the environment can only be modified at run-time.
The next quotation that gets entered after the environment has been
modified will use the new modified environment just like the next
function that reads from a file after the file has been modified will
read the updated file.
> unrecognised word has to be assumed to be a variable that's been
> defined earlier in the program's execution, since the parser can
have
An unrecognized word can be handled in two ways - I've only really
thought about one but I can't see why the other way wouldn't work. The
first way, and the one I prefer, is for every undefined binding to be
just that - undefined. If an undefined word is executed you get an
error (much in the same way you'd get an error if you tried to divide
a number by zero). It's an illegal operation.
The second way is even simpler - reject all undefined bindings at
compile time.
> no knowledge of what the programmer could dynamicly define.
>
> What I'm saying is that if your variable definitions are dynamic,
your
> scoping also must be dynamic.
>
Parsing happens at compile time while words are defined at run time -
the parser has all the information it needs. There are no dynamic
definitions. All bindings happen at run-time but are static, the
language is lexically scoped.
> > In my examples the region is the environment from the point where
the actual word has
> > been defined - because DEF2 is available at run-time that means
it's possible to quote it
> > and hence delay it's execution until the quotation has been
executed/interpreted
> > using the i combinator. The scope of 'a' does not depend on the
nesting of words
> > and quotations at run-time.
>
> I don't understand the last sentance. It seems to contradict the
rest
> of what you've said.
>
In a dynamically scoped language, the scope depends on the nesting of
function calls at run-time. In a statically scoped language it doesn't
nor does it in my examples.
The scope extends from the point where a variable has been bound to a
value (at run-time) to the point where it gets rebound to a new value
(again at run-time).
You can have as many levels of nesting as you like, it will not affect
the scope.
> -Billy
Ivan
William Tanksley, Jr — 2005-03-15 16:33:33
On Tue, 15 Mar 2005 08:24:28 -0000, Ivan Tomac <
e1_t@...> wrote:
>"William Tanksley, Jr" <wtanksleyjr@g...> wrote:
>>>How? In the examples I provided there are no mutable variables. In
>>>the last example 'a' gets rebound at run-time.
>>And the effect is almost exactly the same as a mutable variable --
>>right? The only difference is that already-compiled code will see
>>the old value (or will it, I'm not sure how Joy handles redefinition?).
> No it isn't almost exactly the same as a mutable variable. A variable
> is either mutable or it isn't mutable. In this case it isn't mutable -
> you can't change the state, you can however bind a symbol to a new
> value. In lambda terms it's a lot like let expressions.
If it were like let expressions, it would be syntactic, and thus
determined at compile-time, and thus lexical. But it's not like let
expressions, because it's none of those. I'll look more at this below.
I do agree that it's not neccesarily exactly the same as a mutable
variable -- although for some languages it would be. (For example,
changing a definition in Lisp or Scheme is required to change the
behavior of all existing words -- for them, runtime definitions would
be precisely the same as mutable variables. Forth doesn't work this
way, so runtime definitions in Forth wouldn't be EXACTLY like mutable
variables.)
But you'll notice that I didn't claim an exact analogy -- I said that
they'd be ALMOST the same. The critical question is whether the
differences are important to you. I'd argue based on what you've been
claiming that the differences are irrelevant.
> The code that has already been compiled will see the old value and
> that is the whole point - you can't change it's state, you can't make
> it see new bindings.
This is true for some languages, not true for others. It's a different
set of questions as to how and whether a language allows redefinition.
> How Joy handles redefinition is completely irrelevant. The whole point
> of all this is that Joy is trying to separate defining words from all
> other words even though it isn't necessary for what it's trying to
> achieve.
I agree that Manfred's choice was arbitrary -- he could have chosen
something else, and in fact I would have recommended something else.
But his choice was not unreasonable. Joy is not merely "separating"
defining words; it's deliberately not providing any defining words.
The reason is that Joy is supposed to be an exploration of a new idea
in programming languages; Manfred wanted to reduce that idea as far as
possible to its pure principles.
> > Functional languages have a reason for not allowing mutable
> > variables:
> > they destroy the ability to staticly analyse lambda expressions (the
> > formal term is that they destroy "referential transparency"). This
> > reason applies in this case.
> I agree. However these are not mutable variables.
Even supposing that they're not (and the distinction is purely
artificial, as I'll show below), once again, the *reason* behind the
choice is the same. Whether the cause is runtime-mutable variables or
a runtime-mutable main dictionary, the effect is the same: a loss of
referential transparency.
>>>"In a dynamically scoped language an identifier can be referred
>>>to, not only in the block where it is declared, but also in any
>>>function or procedure called from within that block, even if the
>>>called procedure is declared outside the block."
>>>That is clearly not the case in my examples as a word defined
>>>prior to the definition of 'a' would not be able to access it even
>>>if it is called within the quotation where 'a' has been defined.
>>Why do you assume that a word defined prior to the definition of 'a'
>>wouldn't be able to access it? How could you possibly enforce that?
>>If the definition doesn't happen until runtime, how does your parser
>>know what variables have and haven't been defined?
> A word defined prior to the definition of 'a' would not have 'a' in
> it's environment therefore it would not be able to access it.
Here's the critical point, where I prove that your idea of runtime
redefinitions produces the *exact* same results as mutable variables.
> Defining words are the only words that modify the envionment and
> considering in this case they're no different from other words with
> side-effects (such as IO functions - in fact defining new words is
> really just a form of IO) they can only be executed at run-time.
> Therefore the environment can only be modified at run-time.
> The next quotation that gets entered after the environment has been
> modified will use the new modified environment just like the next
> function that reads from a file after the file has been modified will
> read the updated file.
What if a quotation gets executed twice, once before a redefinition
and once after it? Won't the quotation produce different results, in
spite of containing entirely identical tokens? What if the quotation
itself contains a definition -- won't the result be a redefinition?
Isn't this precisely the behavior you'd expect from a mutable
variable?
But it gets worse.
>>unrecognised word has to be assumed to be a variable that's been
>>defined earlier in the program's execution, since the parser can
>>have
> An unrecognized word can be handled in two ways - I've only really
> thought about one but I can't see why the other way wouldn't work. The
> first way, and the one I prefer, is for every undefined binding to be
> just that - undefined. If an undefined word is executed you get an
> error (much in the same way you'd get an error if you tried to divide
> a number by zero). It's an illegal operation.
This is standard for languages that allow dynamic redefinition. In
fact, it's required, because the compiler-parser (the thing that runs
at compile-time) has a very hard time determining what words are
defined and what aren't. In general, it's an impossible task, unless
you put certain restrictions in place, but I don't see you proposing
any of those.
But in order to allow this behavior for unrecognised words, you have
to allow the exact same behavior for *recognised* words as well. Even
if the compile-time parser somehow "knows" the definition of a word,
as you claim, it still cannot be allowed to foist its opinion of what
the word means on the runtime. The runtime is the true authority; it
can redefine all of the words, and the new meanings must take effect.
Why? Because the compile-time functions actually have *no* knowledge
of definitions, because as you said above, "Therefore the environment
can only be modified at run-time." If the environment is entirely
controlled by the runtime, the static compiler cannot and must not
pretend to have any knowledge of it.
> The second way is even simpler - reject all undefined bindings at
> compile time.
This requires that the compile-time environment have knowledge of
definitions, which in turn requires that definitions be static, which
in turn requires that they not be dynamic, and hence that they not be
runtime.
Well, to be fair, there's nothing wrong with allowing runtime
definitions -- but you'd have to include a complete runtime compiler
as well. This is what Forth does.
>>What I'm saying is that if your variable definitions are dynamic,
>>your scoping also must be dynamic.
> Parsing happens at compile time while words are defined at run time -
> the parser has all the information it needs. There are no dynamic
> definitions. All bindings happen at run-time but are static, the
> language is lexically scoped.
Do you not see the contradiction within the statement "bindings happen
at run-time but are static"? "Is static" is the denial of "happens at
runtime". This is what OO languages talk about when they discuss
"dynamic binding". It means bindings that can change at runtime.
Static bindings are bindings that are set at compile-time.
>>>In my examples the region is the environment from the point where
>>>the actual word has
>>>been defined - because DEF2 is available at run-time that means
>>>it's possible to quote it
>>>and hence delay it's execution until the quotation has been
>>>executed/interpreted
>>>using the i combinator. The scope of 'a' does not depend on the
>>>nesting of words and quotations at run-time.
>>I don't understand the last sentance. It seems to contradict the
>>rest of what you've said.
> In a dynamically scoped language, the scope depends on the nesting of
> function calls at run-time. In a statically scoped language it doesn't
> nor does it in my examples.
But it does. In your examples, a word will only be defined after the
word containing its definition is executed. In a staticly scoped
language, a word is defined everywhere within its lexical scope; in
your examples, words are only defined after their runtime definitions
are executed.
> The scope extends from the point where a variable has been bound to a
> value (at run-time) to the point where it gets rebound to a new value
> (again at run-time).
> You can have as many levels of nesting as you like, it will not affect
> the scope.
EXACTLY. Levels of nesting do not affect the scope; only patterns of
execution affect it. The scoping is (in a sense) chronological; it
starts when the definer is executed, and ends when an undefiner is
executed. It's NOT lexical. In a lexical definition, the scope ranges
over a specific area of the text, and does not vary in any way with
any runtime decisions.
> Ivan
-Billy
John Cowan — 2005-03-15 19:09:44
William Tanksley, Jr scripsit:
> What if a quotation gets executed twice, once before a redefinition
> and once after it? Won't the quotation produce different results, in
> spite of containing entirely identical tokens? What if the quotation
> itself contains a definition -- won't the result be a redefinition?
> Isn't this precisely the behavior you'd expect from a mutable
> variable?
I'm not surprised you're surprised at this notion, but ML-ish languages
really do behave this way. If A makes use of B, then redefining B does
not in any way affect the behavior of A unless and until it too is
redefined.
Conceptually, the environment is an a-list, and new definitions are
pushed down on it, with each new definition capturing the state of the a-list
as it was at definition time. ML does not allow runtime definition, so
the effective scope of a definition is from the point it appears until the
end of the enclosing definition, or the end of input if there is no enclosing
definition.
--
Barry gules and argent of seven and six, John Cowan
on a canton azure fifty molets of the second.
jcowan@...
--blazoning the U.S. flag
http://www.ccil.org/~cowan
William Tanksley, Jr — 2005-03-15 20:10:57
John Cowan <
cowan@...> wrote:
> William Tanksley, Jr scripsit:
> > What if a quotation gets executed twice, once before a redefinition
> > and once after it? Won't the quotation produce different results, in
> I'm not surprised you're surprised at this notion, but ML-ish languages
> really do behave this way. If A makes use of B, then redefining B does
Why did you expect me to be surprised? Why are you bringing up ML in
this context? Yes, I know ML, but I don't see what it has to do with
what I'm saying. ML is both lexically scoped and does not allow
runtime definitions.
> Barry gules and argent of seven and six, John Cowan
-Billy
John Cowan — 2005-03-15 20:29:52
William Tanksley, Jr scripsit:
> Why did you expect me to be surprised? Why are you bringing up ML in
> this context? Yes, I know ML, but I don't see what it has to do with
> what I'm saying. ML is both lexically scoped and does not allow
> runtime definitions.
In ML, you can see the scope rules (at top level) as either a statement about
the static program text or as a statement about the dynamic behavior of the
repl loop. Considering the latter case, a defun/define in Lisp changes
the global environment retroactively, an ML definition does not.
If ML had run-time definitions, they would effectively appear at the bottom of
the program, having no effects on any existing code.
--
Well, I have news for our current leaders John Cowan
and the leaders of tomorrow: the Bill of
jcowan@...
Rights is not a frivolous luxury, in force
http://www.ccil.org/~cowan
only during times of peace and prosperity.
http://www.reutershealth.com
We don't just push it to the side when the going gets tough. --Molly Ivins
William Tanksley, Jr — 2005-03-15 21:26:47
John Cowan <
cowan@...> wrote:
> William Tanksley, Jr scripsit:
> > Why did you expect me to be surprised? Why are you bringing up ML in
> > this context? Yes, I know ML, but I don't see what it has to do with
> > what I'm saying. ML is both lexically scoped and does not allow
> > runtime definitions.
> In ML, you can see the scope rules (at top level) as either a statement about
> the static program text or as a statement about the dynamic behavior of the
> repl loop. Considering the latter case, a defun/define in Lisp changes
> the global environment retroactively, an ML definition does not.
I see your point. Actually, it's the other way around for me -- I
expect the ML behavior, while the Lisp behavior surprises me.
> Well, I have news for our current leaders John Cowan
-Billy
John Cowan — 2005-03-15 21:53:35
William Tanksley, Jr scripsit:
> I see your point. Actually, it's the other way around for me -- I
> expect the ML behavior, while the Lisp behavior surprises me.
I think the difference arises because an ML user at the repl loop is
conceptually supplying a source-code document lazily, whereas a
Lisp user at the repl loop is conceptually whacking on a workspace.
I'm not an APL user, but I think it exhibits Lisp-style behavior and
for the same reason.
--
There is no real going back. Though I John Cowan
may come to the Shire, it will not seem
jcowan@...
the same; for I shall not be the same.
http://www.reutershealth.com
I am wounded with knife, sting, and tooth,
http://www.ccil.org/~cowan
and a long burden. Where shall I find rest? --Frodo
William Tanksley, Jr — 2005-03-15 23:49:25
John Cowan <
cowan@...> wrote:
> William Tanksley, Jr scripsit:
> > I see your point. Actually, it's the other way around for me -- I
> > expect the ML behavior, while the Lisp behavior surprises me.
> I think the difference arises because an ML user at the repl loop is
> conceptually supplying a source-code document lazily, whereas a
> Lisp user at the repl loop is conceptually whacking on a workspace.
> I'm not an APL user, but I think it exhibits Lisp-style behavior and
> for the same reason.
This is probably true, and makes sense. It's interesting that Lisp
historically used dynamic scoping; lexical scoping is fairly new to
it.
APL is something of a different case -- it makes no attempt to be
lambda-pure, like Lisp sometimes does. So using dynamic scope is no
surprise.
By the way, pop quiz: does anyone know of a language that uses dynamic
definitions with lexical scope? I know of a few...
> There is no real going back. Though I John Cowan
-Billy
Ivan Tomac — 2005-03-16 00:46:32
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr"
<wtanksleyjr@g...> wrote:
> > No it isn't almost exactly the same as a mutable variable. A
variable
> > is either mutable or it isn't mutable. In this case it isn't
mutable -
> > you can't change the state, you can however bind a symbol to a new
> > value. In lambda terms it's a lot like let expressions.
>
> If it were like let expressions, it would be syntactic, and thus
> determined at compile-time, and thus lexical. But it's not like let
> expressions, because it's none of those. I'll look more at this
below.
>
You're concentrating on the wrong aspect of the analogy. Let
expressions bind names to values. You cannot change the state of an
existing binding - you can however create a new binding with the same
name.
> I do agree that it's not neccesarily exactly the same as a mutable
> variable -- although for some languages it would be. (For example,
> changing a definition in Lisp or Scheme is required to change the
> behavior of all existing words -- for them, runtime definitions
would
> be precisely the same as mutable variables. Forth doesn't work this
> way, so runtime definitions in Forth wouldn't be EXACTLY like
mutable
> variables.)
>
Lisp and Scheme are impure functional languages that allow assignment
and therefore allow mutable variables.
The language I'm trying to describe does not have mutable variables.
Interesting you brought Forth up because a subset of Forth that does
not offer any words you can use to overwrite the dictionary ('!' and
'C!' for example, but not ',') does exactly what I'm trying to
describe:
: A 3 ;
: B A ;
: A 4 ;
B \ 3 is now on the stack
> But you'll notice that I didn't claim an exact analogy -- I said
that
> they'd be ALMOST the same. The critical question is whether the
> differences are important to you. I'd argue based on what you've
been
> claiming that the differences are irrelevant.
>
But they're not even close - you cannot change the value in an
existing binding. They are more like constants than mutable variables.
> > The code that has already been compiled will see the old value and
> > that is the whole point - you can't change it's state, you can't
make
> > it see new bindings.
>
> This is true for some languages, not true for others. It's a
different
> set of questions as to how and whether a language allows
redefinition.
>
For this particular language it's not a different set of questions.
Bindings are static and can't change and the code itself cannot be
changed either. There is nothing that can be redefined.
> > How Joy handles redefinition is completely irrelevant. The whole
point
> > of all this is that Joy is trying to separate defining words from
all
> > other words even though it isn't necessary for what it's trying to
> > achieve.
>
> I agree that Manfred's choice was arbitrary -- he could have chosen
> something else, and in fact I would have recommended something else.
> But his choice was not unreasonable. Joy is not merely "separating"
> defining words; it's deliberately not providing any defining words.
> The reason is that Joy is supposed to be an exploration of a new
idea
> in programming languages; Manfred wanted to reduce that idea as far
as
> possible to its pure principles.
>
Yes, it is deliberatly not providing any defining words but I feel
that it is doing it for the wrong reasons.
There is no reason that defining words available at run-time must be
able to create mutable variables.
> > > Functional languages have a reason for not allowing mutable
> > > variables:
> > > they destroy the ability to staticly analyse lambda expressions
(the
> > > formal term is that they destroy "referential transparency").
This
> > > reason applies in this case.
>
> > I agree. However these are not mutable variables.
>
> Even supposing that they're not (and the distinction is purely
> artificial, as I'll show below), once again, the *reason* behind the
> choice is the same. Whether the cause is runtime-mutable variables
or
> a runtime-mutable main dictionary, the effect is the same: a loss of
> referential transparency.
>
But the effect isn't the same! Think of it in terms of continuations -
the current environment (or dictionary if you like) gets implicitly
passed to every single word.
The definitions held by the environment are only accessed during
compilation while the environment itself is only modified at run-time.
Whatever you do to the environment has no effect on
quotations/lists/code that has already been compiled - they still use
the old environment and there is nothing you can do to make them use
the new environment.
> > A word defined prior to the definition of 'a' would not have 'a'
in
> > it's environment therefore it would not be able to access it.
>
> Here's the critical point, where I prove that your idea of runtime
> redefinitions produces the *exact* same results as mutable
variables.
>
> > Defining words are the only words that modify the envionment and
> > considering in this case they're no different from other words
with
> > side-effects (such as IO functions - in fact defining new words is
> > really just a form of IO) they can only be executed at run-time.
> > Therefore the environment can only be modified at run-time.
> > The next quotation that gets entered after the environment has
been
> > modified will use the new modified environment just like the next
> > function that reads from a file after the file has been modified
will
> > read the updated file.
>
> What if a quotation gets executed twice, once before a redefinition
> and once after it? Won't the quotation produce different results, in
> spite of containing entirely identical tokens? What if the quotation
> itself contains a definition -- won't the result be a redefinition?
> Isn't this precisely the behavior you'd expect from a mutable
> variable?
>
Have a look at the examples again. I deliberatly constructed them in
such a way to show how such a language would address the issues you
bring up. For conveniance I'll paste both of them in here:
[current-item 4] DEF2
[5] [[current-item [current-item 8] DEF2] times] infra
The end-result on stack is [4 4 4 4 4], not [4 8 8 8 8]. The
environment changes with every iteration due to the DEF2 inside the
loop. However the quotation has already been compiled and cannot be
changed. Second example:
[a 3] DEF2
[b a] DEF2
[a [a 4] DEF2 a] i a b
and on stack you end up with 3 3 4 3.
> >>unrecognised word has to be assumed to be a variable that's been
> >>defined earlier in the program's execution, since the parser can
> >>have
>
> > An unrecognized word can be handled in two ways - I've only really
> > thought about one but I can't see why the other way wouldn't
work. The
> > first way, and the one I prefer, is for every undefined binding
to be
> > just that - undefined. If an undefined word is executed you get an
> > error (much in the same way you'd get an error if you tried to
divide
> > a number by zero). It's an illegal operation.
>
> This is standard for languages that allow dynamic redefinition. In
> fact, it's required, because the compiler-parser (the thing that
runs
> at compile-time) has a very hard time determining what words are
> defined and what aren't. In general, it's an impossible task, unless
> you put certain restrictions in place, but I don't see you proposing
> any of those.
>
That's because no restrictions are necessary as it's lexically scoped
and doesn't allow mutable variables.
There is a reason why I prefer the first way but it's irrelevant to
this discussion - for the purposes of what I'm trying to describe the
second way is probably a better way of handling undefined bindings
although clearly DEF2 wouldn't be possible in such a language and
would have to be replaced by DEF (I'm again referring to the functions
Manfred described in "Further Frequently Asked Questions about Joy").
> But in order to allow this behavior for unrecognised words, you have
> to allow the exact same behavior for *recognised* words as well.
Even
> if the compile-time parser somehow "knows" the definition of a word,
> as you claim, it still cannot be allowed to foist its opinion of
what
> the word means on the runtime. The runtime is the true authority; it
> can redefine all of the words, and the new meanings must take
effect.
> Why? Because the compile-time functions actually have *no* knowledge
> of definitions, because as you said above, "Therefore the
environment
> can only be modified at run-time." If the environment is entirely
> controlled by the runtime, the static compiler cannot and must not
> pretend to have any knowledge of it.
>
Compile time parser has access to the environment in the same way
Forth parser has access to the dictionary and again in the same way
Caml, Scheme, Haskell all have access to the definitions that have
already been defined.
Everything has access to the environment - it helps if you think about
it in terms of a repl loop rather then a separate compile/run cycle.
> > The second way is even simpler - reject all undefined bindings at
> > compile time.
>
> This requires that the compile-time environment have knowledge of
> definitions, which in turn requires that definitions be static,
which
> in turn requires that they not be dynamic, and hence that they not
be
> runtime.
>
Read above.
> Well, to be fair, there's nothing wrong with allowing runtime
> definitions -- but you'd have to include a complete runtime compiler
> as well. This is what Forth does.
>
That is only necessary for dynamically scoped languages. And as far as
I know Forth doesn't need to do this either in the absense of EVALUATE
(there are possibly some special cases considering Forth pretty much
lets you do anything to it.. I think the statement holds for most
Forth programs though).
> >>What I'm saying is that if your variable definitions are dynamic,
> >>your scoping also must be dynamic.
>
> > Parsing happens at compile time while words are defined at run
time -
> > the parser has all the information it needs. There are no dynamic
> > definitions. All bindings happen at run-time but are static, the
> > language is lexically scoped.
>
> Do you not see the contradiction within the statement "bindings
happen
> at run-time but are static"? "Is static" is the denial of "happens
at
> runtime". This is what OO languages talk about when they discuss
> "dynamic binding". It means bindings that can change at runtime.
> Static bindings are bindings that are set at compile-time.
>
Bindings can be static and still happen at run-time. Dynamic binding
would imply that the binding itself can be changed which is not the
case here. A new binding gets created in the environment but all the
old bindings, even the ones with the same name are preserved.
> >>>In my examples the region is the environment from the point where
> >>>the actual word has
> >>>been defined - because DEF2 is available at run-time that means
> >>>it's possible to quote it
> >>>and hence delay it's execution until the quotation has been
> >>>executed/interpreted
> >>>using the i combinator. The scope of 'a' does not depend on the
> >>>nesting of words and quotations at run-time.
>
> >>I don't understand the last sentance. It seems to contradict the
> >>rest of what you've said.
>
> > In a dynamically scoped language, the scope depends on the
nesting of
> > function calls at run-time. In a statically scoped language it
doesn't
> > nor does it in my examples.
>
> But it does. In your examples, a word will only be defined after the
> word containing its definition is executed. In a staticly scoped
> language, a word is defined everywhere within its lexical scope; in
> your examples, words are only defined after their runtime
definitions
> are executed.
>
But the word IS defined everywhere within its lexical scope! I think
the problem is that you're still thinking about this as if the word
gets defined at compile time - as if the lexical scope of a variable
should begin right after DEF or DEF2 has been compiled.
When the parser encounters DEF (or DEF2) within another quotation it
treats it the same way it would treat any other word - it compiles it,
it doesn't execute it.
When you execute the quotation, that's when the binding gets created.
That is the beginning of the scoping region for that binding.
Think about it this way - a quotation that contains a defining word
becomes a defining quotation - becomes a defining word itself.
Any quotations compiled after that particular quotation has been
executed will use the bindings in the new environment created by that
quotation.
> > The scope extends from the point where a variable has been bound
to a
> > value (at run-time) to the point where it gets rebound to a new
value
> > (again at run-time).
> > You can have as many levels of nesting as you like, it will not
affect
> > the scope.
>
> EXACTLY. Levels of nesting do not affect the scope; only patterns of
> execution affect it. The scoping is (in a sense) chronological; it
> starts when the definer is executed, and ends when an undefiner is
> executed. It's NOT lexical. In a lexical definition, the scope
ranges
> over a specific area of the text, and does not vary in any way with
> any runtime decisions.
>
Read what I said above.
> -Billy
Ivan
William Tanksley, Jr — 2005-03-18 18:08:56
Ivan Tomac <
e1_t@...> wrote:
>"William Tanksley, Jr" <wtanksleyjr@g...> wrote:
>>>No it isn't almost exactly the same as a mutable variable. A
>>>variable is either mutable or it isn't mutable. In this case it isn't
>>>mutable - you can't change the state, you can however bind a
>>>symbol to a new value. In lambda terms it's a lot like let
>>>expressions.
>>If it were like let expressions, it would be syntactic, and thus
>>determined at compile-time, and thus lexical. But it's not like let
>>expressions, because it's none of those. I'll look more at this
>>below.
>You're concentrating on the wrong aspect of the analogy.
Possible. I've been known to do that.
>Let
>expressions bind names to values. You cannot change the state of an
>existing binding - you can however create a new binding with the same
>name.
I do agree, of course, that let expressions allow you to "shadow"
variables; the smaller-scoped variable obscures the larger-scoped one
while inside the narrower scope. But this is part of scoping in
general, not just lexical scoping; the fact that your proposal does
the same thing doesn't mean that your proposal is also lexical. It
just means that both are scoping. The critical difference is that
lexical scoping is controlled by the syntax of the language, not by
the semantics. Each defined name has a specific point (byte) in the
source text at which it becomes defined, and a specific point at which
it becomes undefined. With dynamic definitions, there is (sometimes)
no fixed point in the source text at which a variable becomes defined;
rather, there's a step in the execution at which the variable will
become defined. It's conceivable that the variable might not become
defined at all, if the definition is executed within an 'if', and a
later use of the defined name might work because it was appropriately
guarded by a completely different 'if'.
This becomes important for us because if names can become defined at
arbitrary locations within lexical boundaries, then textual
manipulations that should be legal within lexical boundaries become
illegal under certain circumstances. This is obviously critical to a
language that's trying to make theoretical points about formal textual
manipulation.
Now, for a language that's not trying to make any such points, dynamic
definition is absolutely an open option, and I entirely agree with you
that no language should rule it out.
>>I do agree that it's not neccesarily exactly the same as a mutable
>>variable -- although for some languages it would be. (For example,
>>changing a definition in Lisp or Scheme is required to change the
>>behavior of all existing words -- for them, runtime definitions
>>would be precisely the same as mutable variables. Forth doesn't
>>work this way, so runtime definitions in Forth wouldn't be
>>EXACTLY like mutable variables.)
> Lisp and Scheme are impure functional languages that allow assignment
> and therefore allow mutable variables.
Yes; and that's why they don't have a problem with allowing dynamic definitions.
> The language I'm trying to describe does not have mutable variables.
It sounds like a fine language. I have no problem with it. But it's
not Joy -- it doesn't meet the goal of being maximally similar to
classical functional languages, by preserving as much as possible
referential transparency.
> Interesting you brought Forth up because a subset of Forth that does
> not offer any words you can use to overwrite the dictionary ('!' and
> 'C!' for example, but not ',') does exactly what I'm trying to
> describe:
Yup. Forth is one of my favorite languages (although Factor kicks its
butt in many ways :-); but Forth, even if reworked into a more purely
concatenative language, isn't suitable for Joy's goals, partly because
its allowance of dynamic definitions.
Keep in mind the context here -- we're asking whether Joy's decision
to use syntax for definitions is justified. We both agree that it's
not essential to a concatenative language that its definitions be
static; defining new words doesn't mess up concatenativity. However,
you have to consider the specific context of Joy, which is intended
not to be a pure theoretic sibling of Forth, but rather to be the
first concatenative functional language. As such, he considered
referential transparency to be very important.
> > But you'll notice that I didn't claim an exact analogy -- I said
> > that
> > they'd be ALMOST the same. The critical question is whether the
> > differences are important to you. I'd argue based on what you've
> > been claiming that the differences are irrelevant.
> But they're not even close - you cannot change the value in an
> existing binding. They are more like constants than mutable variables.
From the point of view of textual manipulations -- you know, like the
rule that you can always join two valid concatenative programs
together and get another valid concatenative program -- dynamic
bindings act precisely like variables, because the meaning of the name
can change based on when the text gets read in. With lexical scoping,
the only thing that matters is the position of the text within the
source code.
> > > The code that has already been compiled will see the old value and
> > > that is the whole point - you can't change it's state, you can't
> > > make it see new bindings.
> > This is true for some languages, not true for others. It's a
> > different set of questions as to how and whether a language
> > allows redefinition.
> For this particular language it's not a different set of questions.
> Bindings are static and can't change and the code itself cannot be
> changed either. There is nothing that can be redefined.
Joy doesn't work this way (IIRC, quotations can contain undefined
words), and neither does your ideal language (you just said that if a
word is undefined, it would get compiled anyhow in the hopes that a
dynamic definition would be able to determine its meaning before it
had to be run). This is changing the meaning of a word after it's
compiled; and hence, it's dynamic binding.
> > I agree that Manfred's choice was arbitrary -- he could have chosen
> > something else, and in fact I would have recommended something else.
> > But his choice was not unreasonable. Joy is not merely "separating"
> > defining words; it's deliberately not providing any defining words.
> > The reason is that Joy is supposed to be an exploration of a new
> > idea in programming languages; Manfred wanted to reduce that idea
> > as far as possible to its pure principles.
> Yes, it is deliberatly not providing any defining words but I feel
> that it is doing it for the wrong reasons.
> There is no reason that defining words available at run-time must be
> able to create mutable variables.
There is no way to prevent them from creating almost the same effect
as mutable variables. Absolutely no way. The only way you can tell the
effects apart is:
1. Languages with static bindings will be able to tell by comparing
the current value to older bindings that used that name.
2. Languages that provide a way to undefine names will be able to
undefine the new value, and the value of the name will change back to
the old value.
Other than those two very technical points, dynamic definitions create
something that looks like a mutable variable.
> > Even supposing that they're not (and the distinction is purely
> > artificial, as I'll show below), once again, the *reason* behind the
> > choice is the same. Whether the cause is runtime-mutable variables
> > or a runtime-mutable main dictionary, the effect is the same: a loss
> > of referential transparency.
> But the effect isn't the same!
I don't see that. It looks the same, and you haven't shown otherwise.
But even if the effect has MANY more different elements than I've
managed to point out, you STILL lose referential transparency, just
like you do with mutable variables.
> Think of it in terms of continuations -
> the current environment (or dictionary if you like) gets implicitly
> passed to every single word.
Just as with continuations and other extensions to the basic
stack-passing model of concatenative languages, this extension
destroys some of the rules that hold about functional languages.
> The definitions held by the environment are only accessed during
> compilation while the environment itself is only modified at run-time.
> Whatever you do to the environment has no effect on
> quotations/lists/code that has already been compiled - they still use
> the old environment and there is nothing you can do to make them use
> the new environment.
Um... Yes, but...
When can you define a new word? If you can only define new words at
runtime, and runtime is always later than compile-time, then how can
you possibly compile new words? This isn't accurate. Defining new
words takes place at compile-time; that's the only relevant time for
it to happen. If you allow dynamic definitions, you ALSO allow
definitions to be made at runtime. If runtime can never affect
compile-time, those definitions are useless to the source in which
they appear.
You appear to be arguing that runtime definitions can never affect how
code gets compiled. Of course they can! If they couldn't, the
definitions would be useless. Now, I suspect you have some distinction
in your head for when a definition will get used, but nothing you've
said defines when that time is.
> > What if a quotation gets executed twice, once before a redefinition
> > and once after it? Won't the quotation produce different results, in
> > spite of containing entirely identical tokens? What if the quotation
> > itself contains a definition -- won't the result be a redefinition?
> > Isn't this precisely the behavior you'd expect from a mutable
> > variable?
> Have a look at the examples again. I deliberatly constructed them in
> such a way to show how such a language would address the issues you
> bring up. For conveniance I'll paste both of them in here:
> [current-item 4] DEF2
> [5] [[current-item [current-item 8] DEF2] times] infra
> The end-result on stack is [4 4 4 4 4], not [4 8 8 8 8]. The
> environment changes with every iteration due to the DEF2 inside the
> loop. However the quotation has already been compiled and cannot be
> changed. Second example:
It seems you're assuming that compilation proceeds one line at a time,
with all symbols contained in a single line always having a consistent
meaning, no matter what redefinitions appear within that line. This is
a very arbitrary rule, and quite dangerous -- it means that code could
possibly work one way, then you add a word and change the formatting,
and it works another way. I'm going to draw my examples below assuming
(and illustrating) this rule, but keep in mind that my objections are
not limited to it. Any rule you can come up with will result in
dynamic definition being in some way equivalent to mutable variables.
(I can see that another possible rule you might be using is that all
items in a single quotation are compiled at the same time, so no
definitions made during the execution of the quotation will affect it.
My examples also work with that assumption, just in case that's the
rule you were following. I hope for your sake that it wasn't, since
that rule is antithetical to lexical scoping; in lexical scoping
definitions only affect stuff _contained_ within the definition's
lexical body, while by this rule definitions only can affect stuff not
contained within the lexical body that contains the definition.)
First a baseline:
[current-item 4] DEF2
[5] [[current-item [current-item 8] DEF2] times] infra current-item
I'd expect [4 4 4 4 4] 4 from this. But by moving the last word only:
[current-item 4] DEF2
[5] [[current-item [current-item 8] DEF2] times] infra
current-item
I'd expect [4 4 4 4 4] 8, right? Okay, what happens when I do:
[current-item 4] DEF2
[0] [[current-item [current-item 8] DEF2] times] infra
current-item
Notice that the definition is now being executed zero times? Now what
happens? I'd expect [] 4. The problem would be even worse if the
intial definition of current-item were missing, but I'm sure you
expect that.
Now what:
[current-item 4] DEF2
[5] [[current-item [current-item current-item 1 +] DEF2] times] infra
current-item
Interestingly, this produces [4 4 4 4 4] 5, right? Oh, and also; if
you undefine current-item, you'll find that the old value was 5 as
well, since there are actually 5 new definitions.
Suppose you decide that "swap infra" is a useful term for some reason.
What happens to:
[current-item 4] DEF2
[[current-item [current-item current-item 1 +] DEF2] times]
[current-item] swap infra
You'd expect this to be identical to the original program, right? But
it doesn't fit on a single line, so I reformat it:
[current-item 4] DEF2
[[current-item [current-item current-item 1 +] DEF2] times]
[current-item] swap infra
Problem! This isn't the same as the original anymore.
> [a 3] DEF2
> [b a] DEF2
> [a [a 4] DEF2 a] i a b
> and on stack you end up with 3 3 4 3.
These aren't particularly dynamic uses of definition -- surely you
agree that there's no need to have dynamic definition if you don't
actually use it with any conditionals or other dynamic structures.
Now, all of my above examples are assuming your second, disfavored
rule (I quote it in the next quote), that undefined words get
immediately rejected by the compiler (as in Forth). If we use your
first rule (which you prefer), that undefined words get accepted by
the compiler and their meaning is determined by the meaning at the
time the word is run, the problem gets more severe. Either your
compiler is completely dynamicly bound, as Lisp is (in which case your
entire argument is useless and dynamic definitions are exactly the
same as mutable variables), or it's dynamicly bound only for word that
don't have a current definition _for the compiler_, at the time the
compilation starts. Definitions made within the
word/line/unit/whatever being compiled don't count for this; they will
only be seen at runtime.
This will just be confusing.
> > > An unrecognized word can be handled in two ways - I've only really
> > > thought about one but I can't see why the other way wouldn't
> > > work. The
> > > first way, and the one I prefer, is for every undefined binding
> > > to be
> > > just that - undefined. If an undefined word is executed you get an
> > > error (much in the same way you'd get an error if you tried to
> > > divide a number by zero). It's an illegal operation.
> > This is standard for languages that allow dynamic redefinition. In
> > fact, it's required, because the compiler-parser (the thing that
> > runs
> > at compile-time) has a very hard time determining what words are
> > defined and what aren't. In general, it's an impossible task, unless
> > you put certain restrictions in place, but I don't see you proposing
> > any of those.
> That's because no restrictions are necessary as it's lexically scoped
> and doesn't allow mutable variables.
Non sequitur. Lexical scoping isn't meaningful for your language as
you've described it (your language is definitely NOT lexically scoped,
as I've shown above, since new definitons are NOT available within the
block in which they lexically appear), and mutable variables have
nothing whatsoever to do with the problem.
The restrictions that would help would include things like: "dynamic
definitions can only be used in runtime code, not inside of a
definition." That one requirement would ease all of the restrictions.
> > But in order to allow this behavior for unrecognised words, you have
> > to allow the exact same behavior for *recognised* words as well.
By the way, I was technically wrong here. You could behave differently
for recognised and unrecognised words -- but there would be
consequences. Dynamic binding and static binding behave very
differently, and you would be making two visually identical words
behave totally differently. In fact, in a complex application with a
lot of modules, this difference could make the order of loading the
modules critical to the running of the application, since it could
change a word from being staticly defined with one meaning, staticly
defined with another meaning, and dynamicly defined with the meanings
switching depending on which module was most recently called.
> > Well, to be fair, there's nothing wrong with allowing runtime
> > definitions -- but you'd have to include a complete runtime compiler
> > as well. This is what Forth does.
> That is only necessary for dynamically scoped languages.
This has nothing at all to do with dynamic scoping. Dynamic scoping
only requires a runtime dictionary, not a compiler.
> And as far as
> I know Forth doesn't need to do this either in the absense of EVALUATE
> (there are possibly some special cases considering Forth pretty much
> lets you do anything to it.. I think the statement holds for most
> Forth programs though).
You're right on all points here. EVALUATE is, however, Forth's
explicit way to make a binding dynamic.
> Bindings can be static and still happen at run-time. Dynamic binding
> would imply that the binding itself can be changed which is not the
> case here. A new binding gets created in the environment but all the
> old bindings, even the ones with the same name are preserved.
I apologise; you're right. My confusion, my fault.
> > But it does. In your examples, a word will only be defined after the
> > word containing its definition is executed. In a staticly scoped
> > language, a word is defined everywhere within its lexical scope; in
> > your examples, words are only defined after their runtime
> > definitions are executed.
> But the word IS defined everywhere within its lexical scope! I think
> the problem is that you're still thinking about this as if the word
> gets defined at compile time - as if the lexical scope of a variable
> should begin right after DEF or DEF2 has been compiled.
Yes, that's the meaning of the word "lexical scope", as opposed to
other words like "dynamic scope". Lexical scope means that the word is
defined over a single, specific area of the source text. Your
definitions are dynamicly scoped, and some of the suggestions you've
made in an attempt to repair the problem result in something that I
could call anti-lexical scoping -- the definitions are dynamicly
scoped, but they don't become available until after you've _left_ the
lexical scope in which the definitions are contained.
It's true that Forth has a similar rule, but Forth also has the rule
that runtime definitions should take their names from the runtime
source, to make it clear that the runtime is controlling the
situation. For example, the Forth word CREATE makes a new named entry
in the Forth dictionary, but you can't pass the name to it -- it'll
take the name from the source code available at runtime. It's obvious
that the resulting word can't be run by the defining word that created
it, because that word doesn't even know what its name is.
For example, suppose for some reason I wanted to make a new VARIABLE
word. I could use the following definition:
: variable create 1 cells allot ;
To use it, I type:
variable x
0 x !
(and so on.)
The name of the variable is provided by the source code at the
runtime, and the defining word has no need to see it.
This is something of a digression, but it's also somewhat related,
since it shows how dynamic definition can be useful.
> When the parser encounters DEF (or DEF2) within another quotation it
> treats it the same way it would treat any other word - it compiles it,
> it doesn't execute it.
> When you execute the quotation, that's when the binding gets created.
> That is the beginning of the scoping region for that binding.
Yes. It's important to notice that a lexically single definition could
have many scoping regions, and those regions could disappear depending
on the dynamic specifics of the code executing them. This is why we
call it dynamic scoping rather than lexical scoping.
> Ivan
-Billy
John Cowan — 2005-03-18 19:37:07
William Tanksley, Jr scripsit:
> Yes. It's important to notice that a lexically single definition could
> have many scoping regions, and those regions could disappear depending
> on the dynamic specifics of the code executing them. This is why we
> call it dynamic scoping rather than lexical scoping.
It's perhaps worth pointing out (as CLtL does) that the term "dynamic scope"
is a bit of a misnomer. "Dynamic scope" is really dynamic extent (that is,
the object bound to the name becomes inaccessible by it at some point
and indefinite scope (the name can be used anywhere in the code).
See
http://www.supelec.fr/docs/cltl/clm/node43.html for the full details.
--
Unless it was by accident that I had John Cowan
offended someone, I never apologized.
jcowan@...
--Quentin Crisp
http://www.ccil.org/~cowan
William Tanksley, Jr — 2005-03-18 20:33:47
John Cowan <
cowan@...> wrote:
> William Tanksley, Jr scripsit:
> It's perhaps worth pointing out (as CLtL does) that the term "dynamic scope"
> is a bit of a misnomer. "Dynamic scope" is really dynamic extent (that is,
> the object bound to the name becomes inaccessible by it at some point
> and indefinite scope (the name can be used anywhere in the code).
> See http://www.supelec.fr/docs/cltl/clm/node43.html for the full details.
Facinating -- I don't think I've ever heard the distinction made
before. It doesn't seem to me like a really useful distinction,
honestly. According to him, "scope" is the lexical area in which the
definition is available; but if that's the case, the name "lexical
scope" is a redundancy, and "dynamic scope" is a contradiction in
terms. Oh, and he defines "extent" as the time over which a definition
is available; but I don't see how that would ever be useful, since
there's no such thing as "static extent" in those terms, and dynamic
extent is uncomputable.
I'm looking at the definition some more, and I think there's a
confusing point in it. I think "scope" refers to variables, while
"extent" refers to data. Some research on this confirms my general
idea, although it's complicated by the fact than Lisp variables are of
course a first-class part of the language (and hence they can be
considered as data).
In summary, I don't think that page really discusses the issue we're
discussing; it addresses the specifics of how Lisp works (notice that
it claims that dynamic scope is a misnomer simply because Lisp doesn't
actually have dynamic scope, not because dynamic scope is impossible),
and its specificity makes it less than useful for this discussion.
But it's not a bad thing to read -- Lisp is a very good language to
understand better.
> Unless it was by accident that I had John Cowan
-Billy
John Cowan — 2005-03-18 21:37:47
William Tanksley, Jr scripsit:
> Fascinating -- I don't think I've ever heard the distinction made
> before. It doesn't seem to me like a really useful distinction,
> honestly. According to him, "scope" is the lexical area in which the
> definition is available; but if that's the case, the name "lexical
> scope" is a redundancy,
Only in the sense that indefinite scope can be understood as "lexical scope
extending to the whole of the program".
> and "dynamic scope" is a contradiction in terms.
Rather, a shorthand expression for "dynamic extent and indefinite scope".
> Oh, and he defines "extent" as the time over which a definition
> is available; but I don't see how that would ever be useful, since
> there's no such thing as "static extent" in those terms, and dynamic
> extent is uncomputable.
Extent can be either dynamic or indefinite ("static extent" indeed makes
no sense), and we can define indefinite extent as "extent from the dynamic
start of execution to the dynamic end of execution".
> I'm looking at the definition some more, and I think there's a
> confusing point in it. I think "scope" refers to variables, while
> "extent" refers to data.
Scope does indeed refer to variables. Extent refers not to data as such,
but to the *access* to data through a variable. Dynamic extent means
that when some event occurs (such as entering a block or a function
or executing a declaration), the variable is bound to a value, and when
some other event occurs, the variable ceases to be bound to a value.
(In some languages a variable may be bound without having a value,
but there is no point in referencing such a variable until it gets
a value.)
> In summary, I don't think that page really discusses the issue we're
> discussing; it addresses the specifics of how Lisp works (notice that
> it claims that dynamic scope is a misnomer simply because Lisp doesn't
> actually have dynamic scope, not because dynamic scope is impossible),
No, scope (as defined there) cannot be dynamic, because (as you point out)
scope has to do with the static program text.
--
"Well, I'm back." --Sam John Cowan <
jcowan@...>
Ivan Tomac — 2005-03-19 02:17:06
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr"
<wtanksleyjr@g...> wrote:
> >Let
> >expressions bind names to values. You cannot change the state of an
> >existing binding - you can however create a new binding with the
same
> >name.
>
> I do agree, of course, that let expressions allow you to "shadow"
> variables; the smaller-scoped variable obscures the larger-scoped
one
> while inside the narrower scope. But this is part of scoping in
> general, not just lexical scoping; the fact that your proposal does
> the same thing doesn't mean that your proposal is also lexical. It
> just means that both are scoping. The critical difference is that
> lexical scoping is controlled by the syntax of the language, not by
> the semantics. Each defined name has a specific point (byte) in the
> source text at which it becomes defined, and a specific point at
which
> it becomes undefined. With dynamic definitions, there is (sometimes)
> no fixed point in the source text at which a variable becomes
defined;
Although it may not be apparent, the scoping actually IS defined in
the source text.
Consider a repl loop in Caml or SML, whichever you prefer. You type in
an expression, hit enter and the expression is evaluated. You could
say it happens at run-time.
Now consider this:
let f () = print_string "test";;
You could say that binding of f occured at run-time considering that
the repl loop evaluates expressions entered by the programmer.
So then where/when does compilation happen? Between = and ;;. Note
that without the two parens print_string would have been evaluated
right away - there would be no compiling - and f would have been bound
to a unit value - ().
How is that similar to a concatenative language? Simple - imagine a
repl loop in a concatenative language that simply evaluates
expressions. The above example could be written as:
[f "test" put] DEF2
or
"f" ["test" put] DEF
The binding happens at run-time again - it's the code inside the
quotation that gets compiled - it's execution is delayed.
But what about using DEF and DEF2 inside quotations? Again simple:
[g [f "test" put] DEF2] DEF2
f does not get defined until g has already been executed.
Furthermore the following code:
[g [f "test" put] DEF2] DEF2
... code ...
g
... code ...
... new definition or quotation that uses f ...
can be rewritten as:
[g] DEF2
... code ...
[f "test" put] DEF2
... code ...
... new definition or quotation that uses f ...
> rather, there's a step in the execution at which the variable will
> become defined. It's conceivable that the variable might not become
> defined at all, if the definition is executed within an 'if', and a
> later use of the defined name might work because it was
appropriately
> guarded by a completely different 'if'.
>
Yes but it only affects new quotations entered by the user - no
existing code is affected. In fact you just described one advantage of
this approach that I haven't considered before!
It allows you to do things that in most other languages you have to
use a preprocessor for - for example compile two different versions of
a function depending on what OS you're running.
> This becomes important for us because if names can become defined at
> arbitrary locations within lexical boundaries, then textual
> manipulations that should be legal within lexical boundaries become
> illegal under certain circumstances. This is obviously critical to a
> language that's trying to make theoretical points about formal
textual
> manipulation.
>
Ah, but the names do not get bound until run-time! Therefore at
compile time you're still within the scope of the previous
environment. Textual manipulations are not affected.
> Now, for a language that's not trying to make any such points,
dynamic
> definition is absolutely an open option, and I entirely agree with
you
> that no language should rule it out.
>
How is it dynamic? You cannot redefine anything - the language does
not allow mutable variables.
Give me an example so I can see what part of what I'm trying to
explain is unclear.
> > Lisp and Scheme are impure functional languages that allow
assignment
> > and therefore allow mutable variables.
>
> Yes; and that's why they don't have a problem with allowing dynamic
definitions.
>
I actually do have a problem with dynamic definitions.
> > The language I'm trying to describe does not have mutable
variables.
>
> It sounds like a fine language. I have no problem with it. But it's
> not Joy -- it doesn't meet the goal of being maximally similar to
> classical functional languages, by preserving as much as possible
> referential transparency.
>
How so? Again give me an example. I thought the examples I've provided
along with the explainations were sufficient to prove my point but I
was apparently wrong. Give me an example of how you could define a
mutable variable in the langauge.
> > Interesting you brought Forth up because a subset of Forth that
does
> > not offer any words you can use to overwrite the dictionary ('!'
and
> > 'C!' for example, but not ',') does exactly what I'm trying to
> > describe:
>
> Yup. Forth is one of my favorite languages (although Factor kicks
its
> butt in many ways :-); but Forth, even if reworked into a more
purely
> concatenative language, isn't suitable for Joy's goals, partly
because
> its allowance of dynamic definitions.
>
Without words that can be used to overwrite the dictionary (but not
append to the dictionary!) how can Forth have dynamic definitions?
Could you give an example?
> Keep in mind the context here -- we're asking whether Joy's decision
> to use syntax for definitions is justified. We both agree that it's
> not essential to a concatenative language that its definitions be
> static; defining new words doesn't mess up concatenativity. However,
> you have to consider the specific context of Joy, which is intended
> not to be a pure theoretic sibling of Forth, but rather to be the
> first concatenative functional language. As such, he considered
> referential transparency to be very important.
>
I'm just as interested in theory and for the same reason I do not want
to break referential transparency. And I still fail to see how does
what I described break referential transparency.
> > But they're not even close - you cannot change the value in an
> > existing binding. They are more like constants than mutable
variables.
>
> From the point of view of textual manipulations -- you know, like
the
> rule that you can always join two valid concatenative programs
> together and get another valid concatenative program -- dynamic
> bindings act precisely like variables, because the meaning of the
name
> can change based on when the text gets read in. With lexical
scoping,
> the only thing that matters is the position of the text within the
> source code.
>
But that only affects newly entered definitions, not existing ones -
how can you change the meaning then? A mutable variable is mutable
because it's contents can change - because the compiler cannot make
assumptions on whether it's currently bound to 1 or 3. That is not the
case with static bindings - once 'a' is bound to 3 the compiler knows
that it can safely replace every occurance of 'a' with 3.
As compiling and execution are separate and new bindings are defined
only during execution, after execution the environment is static. You
cannot add any more bindings at compile-time. Therefore the compiler
can safely assume that anything in the current environment will not
change. It can safely replace every occurance of 'a' with 3. I really
cannot see where you see mutable variables in this.
Note also that concatenative properties of the language (as I
understand them anyway) have not been broken - you can still split a
quotation into two at any point and still get 2 valid programs. You
can also concatenate any two quotations and again get a valid program.
> > For this particular language it's not a different set of
questions.
> > Bindings are static and can't change and the code itself cannot be
> > changed either. There is nothing that can be redefined.
>
> Joy doesn't work this way (IIRC, quotations can contain undefined
> words), and neither does your ideal language (you just said that if
a
> word is undefined, it would get compiled anyhow in the hopes that a
> dynamic definition would be able to determine its meaning before it
> had to be run). This is changing the meaning of a word after it's
> compiled; and hence, it's dynamic binding.
>
I said no such thing.
I said that words that have not been defined can be handled in two
ways - I said I personally prefer the way where undefined bindings get
compiled in anyway (I didn't give a reason for this because it would
take too long to explain and is completely irrelevant to the
discussion - it has to do with abstract data structures) - the other
way which I said is more relevant to the discussion is the one where
undefined bindings get rejected at compile time.
I never said anything about dynamic (re)definition of words. In fact I
keep pointing over and over again that you cannot re-define words at
run-time - you can only create new bindings - the existing ones remain
intact. For the purpose of compiling new definitions only the most
recent binding is used.
> > Yes, it is deliberatly not providing any defining words but I feel
> > that it is doing it for the wrong reasons.
> > There is no reason that defining words available at run-time must
be
> > able to create mutable variables.
>
> There is no way to prevent them from creating almost the same effect
> as mutable variables. Absolutely no way. The only way you can tell
the
> effects apart is:
>
> 1. Languages with static bindings will be able to tell by comparing
> the current value to older bindings that used that name.
Which is exactly what happens here. The environment is static at
compile time - new bindings are only added at run-time.
> 2. Languages that provide a way to undefine names will be able to
> undefine the new value, and the value of the name will change back
to
> the old value.
>
> Other than those two very technical points, dynamic definitions
create
> something that looks like a mutable variable.
>
I have not once said that the language can undefine anything. Quite
the opposite. Once created, a binding cannot be removed. It may be
inaccessible by the programmer due to newly created bindings of the
same name but it's still accessible by any quotation/definition that
was compiled at the time before the new binding was created.
> > But the effect isn't the same!
>
> I don't see that. It looks the same, and you haven't shown
otherwise.
> But even if the effect has MANY more different elements than I've
> managed to point out, you STILL lose referential transparency, just
> like you do with mutable variables.
>
> > Think of it in terms of continuations -
> > the current environment (or dictionary if you like) gets
implicitly
> > passed to every single word.
>
> Just as with continuations and other extensions to the basic
> stack-passing model of concatenative languages, this extension
> destroys some of the rules that hold about functional languages.
>
Then once again I'll ask you to give me an example of how such a
language would create a mutable variable so that I can clear up any
misconception you may have about what I've said so far.
> > The definitions held by the environment are only accessed during
> > compilation while the environment itself is only modified at
run-time.
> > Whatever you do to the environment has no effect on
> > quotations/lists/code that has already been compiled - they still
use
> > the old environment and there is nothing you can do to make them
use
> > the new environment.
>
> Um... Yes, but...
>
> When can you define a new word? If you can only define new words at
> runtime, and runtime is always later than compile-time, then how can
> you possibly compile new words? This isn't accurate. Defining new
> words takes place at compile-time; that's the only relevant time for
> it to happen. If you allow dynamic definitions, you ALSO allow
> definitions to be made at runtime. If runtime can never affect
> compile-time, those definitions are useless to the source in which
> they appear.
>
No run-time is not always later then compile-time. In a repl loop you
can compile a number of definitions, then execute some others, then
compile more definitions. Which is why I said it helps if you think
about this in terms of a repl loop rather then a separate compile/run
cycle.
And no - defining new words doesn't have anything to do with compile
time - compiling happens at compile-time. I would argue that binding
or defining new words happens after compiling in every language. They
are different even though most languages don't treat them as such. In
other words a quotation or a code block first has to be compiled
before it can be bound to a name.
There are languages that don't have bindings at all and don't let you
create any for that matter.
> You appear to be arguing that runtime definitions can never affect
how
> code gets compiled. Of course they can! If they couldn't, the
> definitions would be useless. Now, I suspect you have some
distinction
> in your head for when a definition will get used, but nothing you've
> said defines when that time is.
No, I'm arguing that run-time definitions cannot change any existing
bindings, only create new ones. There is a difference.
Again think let expressions - in a repl loop they get evaluated at
run-time.
> > Have a look at the examples again. I deliberatly constructed them
in
> > such a way to show how such a language would address the issues
you
> > bring up. For conveniance I'll paste both of them in here:
>
> > [current-item 4] DEF2
>
> > [5] [[current-item [current-item 8] DEF2] times] infra
>
> > The end-result on stack is [4 4 4 4 4], not [4 8 8 8 8]. The
> > environment changes with every iteration due to the DEF2 inside
the
> > loop. However the quotation has already been compiled and cannot
be
> > changed. Second example:
>
> It seems you're assuming that compilation proceeds one line at a
time,
> with all symbols contained in a single line always having a
consistent
> meaning, no matter what redefinitions appear within that line. This
is
> a very arbitrary rule, and quite dangerous -- it means that code
could
> possibly work one way, then you add a word and change the
formatting,
> and it works another way. I'm going to draw my examples below
assuming
> (and illustrating) this rule, but keep in mind that my objections
are
> not limited to it. Any rule you can come up with will result in
> dynamic definition being in some way equivalent to mutable
variables.
>
No - compilation proceeds one top-level quotation at a time. In other
words it starts when you enter '[' and ends when you enter a matching
']'. And all the symbols inside the quotation DO have a consistent
meaning - that's the whole point of static bindings and that's why I
keep saying over and over again that at compile time the environment
is static.
Redefinitions are not redefinitions until run-time (in fact they're
not redefinitions at all - they create new bindings, don't change
existing ones). At compile time DEF and DEF2 are not treated any
different to any other word - in fact you could say they're just
symbols with no meaning until run-time and that the quotation they're
used in has the current environment associated with it and out of that
envrionment each symbol gets a meaning or a value at run-time!
As for your statement about reformatting code I'm not sure I
understand what you mean? Adding a word and changing formatting would
change the meaning of code in any language.
> (I can see that another possible rule you might be using is that all
> items in a single quotation are compiled at the same time, so no
> definitions made during the execution of the quotation will affect
it.
> My examples also work with that assumption, just in case that's the
> rule you were following. I hope for your sake that it wasn't, since
> that rule is antithetical to lexical scoping; in lexical scoping
> definitions only affect stuff _contained_ within the definition's
> lexical body, while by this rule definitions only can affect stuff
not
> contained within the lexical body that contains the definition.)
>
That's exactly the rule I'm using and no, it does not conflict with
the definition of static scoping (it conflicts with the definition of
static nested scoping however but not static scoping!).
As I've already mentioned defining a word is not neccessarily the same
as compiling - defining or binding always happens after compiling in
both dynamicly and statically scoped languages even though that is not
apparent in most statically scoped languages. Binding is always
delayed until the word/function/code block/quotation has already been
compiled!
To put it another way - there are only two forms of scoping - dynamic
and lexical/static - if this isn't dynamic scoping then it can only be
static/lexical and it isn't dynamic scoping (hopefully I'll eventually
manage to convice you of that).
> First a baseline:
>
> [current-item 4] DEF2
> [5] [[current-item [current-item 8] DEF2] times] infra current-item
>
> I'd expect [4 4 4 4 4] 4 from this. But by moving the last word
only:
Wrong - you'd get [4 4 4 4 4] 8. Compilation happens between '[' and
']' at the top level. infra happens at run-time and current-item at
the end of the line happens after the new binding has already been
created.
>
> [current-item 4] DEF2
> [5] [[current-item [current-item 8] DEF2] times] infra
> current-item
>
> I'd expect [4 4 4 4 4] 8, right? Okay, what happens when I do:
>
> [current-item 4] DEF2
> [0] [[current-item [current-item 8] DEF2] times] infra
> current-item
>
> Notice that the definition is now being executed zero times? Now
what
> happens? I'd expect [] 4.
Correct. Note that that is exactly the same as this:
[current-item 4] DEF2
[0] [[current-item] times] infra current-item
while the former can be rewritten as:
[current-item 4] DEF2
[5] [[current-item] times] infra
[current-item 8] DEF2
current-item
> The problem would be even worse if the
> intial definition of current-item were missing, but I'm sure you
> expect that.
>
> Now what:
>
> [current-item 4] DEF2
> [5] [[current-item [current-item current-item 1 +] DEF2] times]
infra
> current-item
>
> Interestingly, this produces [4 4 4 4 4] 5, right?
>Oh, and also; if
> you undefine current-item, you'll find that the old value was 5 as
> well, since there are actually 5 new definitions.
>
How can you undefine current-item? Remember that bindings cannot be
changed once created.
> Suppose you decide that "swap infra" is a useful term for some
reason.
> What happens to:
>
> [current-item 4] DEF2
> [[current-item [current-item current-item 1 +] DEF2] times]
> [current-item] swap infra
>
> You'd expect this to be identical to the original program, right?
But
> it doesn't fit on a single line, so I reformat it:
>
> [current-item 4] DEF2
> [[current-item [current-item current-item 1 +] DEF2] times]
> [current-item] swap infra
>
> Problem! This isn't the same as the original anymore.
>
Problem is that you're assuming compilation happens on a line-by-line
basis. Read above.
> > [a 3] DEF2
> > [b a] DEF2
> > [a [a 4] DEF2 a] i a b
> > and on stack you end up with 3 3 4 3.
>
> These aren't particularly dynamic uses of definition -- surely you
> agree that there's no need to have dynamic definition if you don't
> actually use it with any conditionals or other dynamic structures.
>
Alright - give me a better example. Whether you use conditionals or
not it doesn't make a difference - you still can't have mutable
variables. You can have conditional compilation which is quite
different and again only affects words defined after the word that
creates the definition has been executed.
> Now, all of my above examples are assuming your second, disfavored
> rule (I quote it in the next quote), that undefined words get
> immediately rejected by the compiler (as in Forth). If we use your
> first rule (which you prefer), that undefined words get accepted by
> the compiler and their meaning is determined by the meaning at the
> time the word is run, the problem gets more severe. Either your
> compiler is completely dynamicly bound, as Lisp is (in which case
your
> entire argument is useless and dynamic definitions are exactly the
> same as mutable variables), or it's dynamicly bound only for word
that
> don't have a current definition _for the compiler_, at the time the
> compilation starts. Definitions made within the
> word/line/unit/whatever being compiled don't count for this; they
will
> only be seen at runtime.
>
> This will just be confusing.
>
No, again you're assuming that just because the symbol is undefined at
compile time it will be redefined at run-time which is not the case -
it will remain undefined - anyway I've already addressed this.
> > That's because no restrictions are necessary as it's lexically
scoped
> > and doesn't allow mutable variables.
>
> Non sequitur. Lexical scoping isn't meaningful for your language as
> you've described it (your language is definitely NOT lexically
scoped,
> as I've shown above, since new definitons are NOT available within
the
> block in which they lexically appear), and mutable variables have
> nothing whatsoever to do with the problem.
Read above.
>
> The restrictions that would help would include things like: "dynamic
> definitions can only be used in runtime code, not inside of a
> definition." That one requirement would ease all of the
restrictions.
>
But there are NO dynamic definitions! Bindings occur at run-time but
they are completely static! You cannot change them once they have been
created.
> > > Well, to be fair, there's nothing wrong with allowing runtime
> > > definitions -- but you'd have to include a complete runtime
compiler
> > > as well. This is what Forth does.
>
> > That is only necessary for dynamically scoped languages.
>
> This has nothing at all to do with dynamic scoping. Dynamic scoping
> only requires a runtime dictionary, not a compiler.
>
Good point. Still the language in question would not need a run-time
compiler just like Forth without EVALUATE would not need one.
> > Bindings can be static and still happen at run-time. Dynamic
binding
> > would imply that the binding itself can be changed which is not
the
> > case here. A new binding gets created in the environment but all
the
> > old bindings, even the ones with the same name are preserved.
>
> I apologise; you're right. My confusion, my fault.
>
Ah, but you still insist that the language is dynamicly scoped even
though the bindings are static! Do you not see that all the compiling
happens at compile time (therefore there is no need for a run-time
compiler) but all the bindings happen at run-time and are static?
You cannot change existing bindings and you cannot undefine existing
bindings. You can only create new ones and they only only affect newly
defined quotations much like binding a name to an expression in a let
expression will only affect the code that follows the binding.
How can the language be dynamically scoped then?
> > But the word IS defined everywhere within its lexical scope! I
think
> > the problem is that you're still thinking about this as if the
word
> > gets defined at compile time - as if the lexical scope of a
variable
> > should begin right after DEF or DEF2 has been compiled.
>
> Yes, that's the meaning of the word "lexical scope", as opposed to
> other words like "dynamic scope". Lexical scope means that the word
is
> defined over a single, specific area of the source text. Your
> definitions are dynamicly scoped, and some of the suggestions you've
> made in an attempt to repair the problem result in something that I
> could call anti-lexical scoping -- the definitions are dynamicly
> scoped, but they don't become available until after you've _left_
the
> lexical scope in which the definitions are contained.
>
No, and I will again point out that compiling quotations/code and
binding a name are not the same. They are two disctinct operations
even though most languages don't treat them as such. Binding always
happens after compiling.
> > When the parser encounters DEF (or DEF2) within another quotation
it
> > treats it the same way it would treat any other word - it
compiles it,
> > it doesn't execute it.
> > When you execute the quotation, that's when the binding gets
created.
> > That is the beginning of the scoping region for that binding.
>
> Yes. It's important to notice that a lexically single definition
could
> have many scoping regions, and those regions could disappear
depending
> on the dynamic specifics of the code executing them. This is why we
> call it dynamic scoping rather than lexical scoping.
>
No - what you are referring to in this case would be conditional
compilation. I should state that in this case 'compilation' is a bit
misleading as it's really just conditional binding of names but the
traditional term is conditional compilation.
Dynamic scoping has nothing to do with that - dynamic scoping would
mean that once I bind 'a' to say 3, and use it within 'b', and then
later yet use 'b' within 'c' which redefines 'a' to 5, 'b' would use
'a' as if it has been defined as 5 to start with.
That's a bit confusing so I'll illustrate that with an example:
[a 3] DEF2
[b a] DEF2
[c [a 5] DEF2 b] DEF2
c b
and on stack you end up with 5 3 - that's what would happen in a
dynamically scoped language (recall that the scope in dynamically
scoped languages depends on the nesting of function calls at
run-time).
In a statically scoped language I'm trying to describe the above code
would put 3 3 on stack.
> -Billy
Ivan
William Tanksley, Jr — 2005-03-20 05:15:31
Ivan Tomac <
e1_t@...> wrote:
I'm going to skip a little; I think we're starting to wander. Sorry.
>"William Tanksley, Jr" <wtanksleyjr@g...> wrote:
>>Now, for a language that's not trying to make any such points,
>>dynamic definition is absolutely an open option, and I entirely
>>agree with you that no language should rule it out.
> How is it dynamic?
Dynamic essentially means "happens at runtime". These definitions
happen at runtime; therefore they are dynamic. (Actually, "dynamic"
means that the full results can't be known at compile-time, but this
definitions also applies.)
> You cannot redefine anything - the language does
> not allow mutable variables.
Wait, you're now saying that no name in the source can ever be
assigned to a value once it's been previously assigned? So once I
assign 3 to the symbol x, x will only and exclusively have the value
3, no matter what happens? Nobody else can ever define a variable or
function using the symbol 'x' ever again?
If this is true, it's the most restrictive proposal I've ever heard.
> Give me an example so I can see what part of what I'm trying to
> explain is unclear.
I'll clarify the ones I gave. You corrected one of my examples, but
the rest still stand.
> was apparently wrong. Give me an example of how you could define a
> mutable variable in the langauge.
Just so we understand: I can't, of course, do that. I never claimed
to. I claimed that the dynamic (runtime) definitions you were
proposing would interfere with textual manipulations in the same way
that mutable variables do.
> Without words that can be used to overwrite the dictionary (but not
> append to the dictionary!) how can Forth have dynamic definitions?
> Could you give an example?
There's a problem with trying that -- Forth has only one linear
dictionary, so while one word is being compiled, no other compilation
may take place. So I can't define words while another word is being
defined. The second problem is that IF and THEN and so on are only
legal within a definition, so I can't do truly dynamic things with the
results.
So the limitations of Forth make this impossible. Your language
doesn't propose any of those limitations.
>>>But they're not even close - you cannot change the value in an
>>>existing binding. They are more like constants than mutable
>>>variables.
>>From the point of view of textual manipulations -- you know, like
>>the rule that you can always join two valid concatenative programs
>>together and get another valid concatenative program -- dynamic
>>bindings act precisely like variables, because the meaning of the
>>name can change based on when the text gets read in. With
>>lexical scoping,
>>the only thing that matters is the position of the text within the
>>source code.
> But that only affects newly entered definitions, not existing ones -
> how can you change the meaning then?
_Textually_, there is no difference between "newly entered" and
"existing" definitions. If two identical words at the same lexical
level have different meanings due to dynamic operation of the
language, the compiler cannot use static analysis.
> As compiling and execution are separate and new bindings are defined
> only during execution, after execution the environment is static. You
> cannot add any more bindings at compile-time. Therefore the compiler
> can safely assume that anything in the current environment will not
> change. It can safely replace every occurance of 'a' with 3. I really
> cannot see where you see mutable variables in this.
You just described conditional compilation. That's dynamic. I show
below that loops can also dynamicly change the final values bound to
variables. The compiler can't pretend the definitions are static; in
static terms, the compiler can't know which definition will be chosen
from the IF.
> Note also that concatenative properties of the language (as I
> understand them anyway) have not been broken - you can still split a
> quotation into two at any point and still get 2 valid programs. You
> can also concatenate any two quotations and again get a valid program.
Okay, here's an example.
[x 0] DEF2
[x [x 1] DEF2]
[x [x 2] DEF2]
i swap i
This should produce (0 1) on the stack, unless I got that wrong. Now,
by concatenative theory, you should be able to concatenate those two
quotations together, and get the exact effect of calling them one
after the other (the swap, of course, just buries the first function's
result and gets at the address of the second function, so the
concatenation shouldn't change the final result):
[x 0] DEF2
[x [x 1] DEF2 x [x 2] DEF2]
i
Now the result is (0 0).
The result has changed, but nothing's happened that should be
violating concatenativity, so the result shouldn't change.
>>Joy doesn't work this way (IIRC, quotations can contain undefined
>>words), and neither does your ideal language (you just said that if
>>a
>>word is undefined, it would get compiled anyhow in the hopes that a
>>dynamic definition would be able to determine its meaning before it
>>had to be run). This is changing the meaning of a word after it's
>>compiled; and hence, it's dynamic binding.
> I said no such thing.
Actually, that's what you said, and what you say below.
> I said that words that have not been defined can be handled in two
> ways - I said I personally prefer the way where undefined bindings get
> compiled in anyway
Okay, and then what happens? "Undefined results"? No, you expect the
programmer to define the name before the code gets run. You said so
yourself. And when the code is run, THEN the undefined word will take
on the current meaning. Who decides what the current meaning is? Not
the compiler, since the compiler only knows what was defined at the
time the word was used. Here's an example:
Assume "print" is defined to print a number. x is not defined.
[getx x] DEF2
[x 0] DEF2
[getx [x getx 1 +] DEF2] 3 times
(I'm not fluent in Joy -- I probably got "times" wrong. Please assume
it works the way I wrote it.)
That code will leave (0 1 2) on the stack and x will be 3, if I read
your proposal right, because the 'x' in 'getx' has to be rebound when
it's executed. This has the FULL effect of a mutable variable.
> the other
> way which I said is more relevant to the discussion is the one where
> undefined bindings get rejected at compile time.
It's easier to reason about languages this way, because all of the
bindings are static, rather than some being static and some being
dynamic.
> I never said anything about dynamic (re)definition of words. In fact I
> keep pointing over and over again that you cannot re-define words at
> run-time - you can only create new bindings - the existing ones remain
> intact. For the purpose of compiling new definitions only the most
> recent binding is used.
Irrelevant. The old binding is no longer available. Thus, the symbol
has been redefined. No, that redefinition is not retroactive (as it
would be with mutable variables or dynamic binding), but its scope is
not fixed to any lexical area; its scope depends entirely on how the
program's execution flows.
> > 2. Languages that provide a way to undefine names will be able to
> > undefine the new value, and the value of the name will change back
> > to the old value.
> > Other than those two very technical points, dynamic definitions
> > create
> > something that looks like a mutable variable.
> I have not once said that the language can undefine anything.
And I didn't say you did. I said that IF a language does provide a way
to undefine, THEN dynamic definitions can sometimes be told apart from
variable assignments, because variable assignments cannot be undone,
but an undefinition is the undoing of a definition.
But thanks for providing another bit of information about how your
language works.
> > You appear to be arguing that runtime definitions can never affect
> how
> > code gets compiled. Of course they can! If they couldn't, the
> > definitions would be useless. Now, I suspect you have some
> distinction
> > in your head for when a definition will get used, but nothing you've
> > said defines when that time is.
>
> No, I'm arguing that run-time definitions cannot change any existing
> bindings, only create new ones. There is a difference.
I'm not arguing about this. Forget about it. I agree entirely. Please
don't ever read anything I write in the future as if it were a
statement contradicting that. I accept that your language doesn't have
dynamic bindings. I assert that it does have dynamic definitions.
> No - compilation proceeds one top-level quotation at a time. In other
Ah, okay. So my second guess was better than my first. Sorry.
> Redefinitions are not redefinitions until run-time (in fact they're
> not redefinitions at all - they create new bindings, don't change
> existing ones).
Pardon, but they ARE redefinitions. They don't change past uses of the
bindings, but they DO change the meaning of the text.
> > that rule is antithetical to lexical scoping; in lexical scoping
> > definitions only affect stuff _contained_ within the definition's
> > lexical body, while by this rule definitions only can affect stuff
> > not
> > contained within the lexical body that contains the definition.)
> That's exactly the rule I'm using and no, it does not conflict with
> the definition of static scoping (it conflicts with the definition of
> static nested scoping however but not static scoping!).
According to FOLDOC, the two mean the same thing. Nested scoping just
means that you can redefine a word and when you undefine it the old
word will still be there. That's pretty much a requirement of lexical
scoping; in fact, FOLDOC wasn't able to come up with any examples of
lexical scoping that weren't also nested lexical.
> As I've already mentioned defining a word is not neccessarily the same
> as compiling
I don't know why you mention it; it appears to have no relation to
anything I've ever said.
> To put it another way - there are only two forms of scoping - dynamic
> and lexical/static - if this isn't dynamic scoping then it can only be
> static/lexical and it isn't dynamic scoping (hopefully I'll eventually
> manage to convice you of that).
I don't see how. It's definitely dynamic, since it happens at runtime.
See
http://foldoc.hld.c64.org/foldoc.cgi?lexical+scope for definitions
that work for me.
> > First a baseline:
> > [current-item 4] DEF2
> > [5] [[current-item [current-item 8] DEF2] times] infra current-item
> >
> > I'd expect [4 4 4 4 4] 4 from this. But by moving the last word
> only:
> Wrong - you'd get [4 4 4 4 4] 8. Compilation happens between '[' and
Whoops, this is the one I got dead wrong. I actually thought I'd
cleaned it out after I realised that you might be using quotations as
your compilation boundaries. My bad.
> > [current-item 4] DEF2
> > [5] [[current-item [current-item 8] DEF2] times] infra
> > current-item
> > I'd expect [4 4 4 4 4] 8, right? Okay, what happens when I do:
Okay, so this one is the baseline.
> > [current-item 4] DEF2
> > [0] [[current-item [current-item 8] DEF2] times] infra
> > current-item
> > Notice that the definition is now being executed zero times? Now
> > what
> > happens? I'd expect [] 4.
> Correct.
But see the problem? The empty list is fine, because it's obviously
the result of the normal operation of infra and time; but the 4 is
totally unexpected. The compiler cannot staticly guess that it should
be 4. Lexically, there was a definition preceding it that should have
changed it to 8; but dynamicly the program chose not to execute that
definition.
> Note that that is exactly the same as this:
> [current-item 4] DEF2
> [0] [[current-item] times] infra current-item
Except for the lack of any definitions, right? Since that's what we're
discussing, I think that's a pretty painful omission.
> while the former can be rewritten as:
> [current-item 4] DEF2
> [5] [[current-item] times] infra
> [current-item 8] DEF2
> current-item
What if the [5] were dynamicly chosen -- for example, from user input,
or read in from a file?
> > The problem would be even worse if the
> > intial definition of current-item were missing, but I'm sure you
> > expect that.
> > Now what:
> > [current-item 4] DEF2
> > [5] [[current-item [current-item current-item 1 +] DEF2] times]
> > infra current-item
> > Interestingly, this produces [4 4 4 4 4] 5, right?
> >Oh, and also; if
> > you undefine current-item, you'll find that the old value was 5 as
> > well, since there are actually 5 new definitions.
You didn't respond to the example, in which the value of current-item
mutates to 5.
> > You'd expect this to be identical to the original program, right?
> > [current-item 4] DEF2
> > [[current-item [current-item current-item 1 +] DEF2] times]
> > [current-item] swap infra
> > Problem! This isn't the same as the original anymore.
> Problem is that you're assuming compilation happens on a line-by-line
> basis. Read above.
My explanation did assume that. But the program as written does show
what I wanted to show, that a "safe" manipulation is actually unsafe.
Exchanging two quotations in the source and then inserting a swap
should produce the same result; but if the second quotation gets
executed first and includes a redefinition of a variable in the first
quotation, the redefinition changes how the second quotation gets
compiled.
This could get much much worse, if the two quotations were in
different libraries.
> > Now, all of my above examples are assuming your second, disfavored
> > rule (I quote it in the next quote), that undefined words get
> > immediately rejected by the compiler (as in Forth). If we use your
> > first rule (which you prefer), that undefined words get accepted by
> > the compiler and their meaning is determined by the meaning at the
> > time the word is run, the problem gets more severe. Either your
> > compiler is completely dynamicly bound, as Lisp is (in which case
> your
> > entire argument is useless and dynamic definitions are exactly the
> > same as mutable variables), or it's dynamicly bound only for word
> that
> > don't have a current definition _for the compiler_, at the time the
> > compilation starts. Definitions made within the
> > word/line/unit/whatever being compiled don't count for this; they
> will
> > only be seen at runtime.
> >
> > This will just be confusing.
> No, again you're assuming that just because the symbol is undefined at
> compile time it will be redefined at run-time which is not the case -
> it will remain undefined - anyway I've already addressed this.
No, you haven't addressed it; you just claimed I was wrong, and then
when you tried to explain you stopped the explanation exactly when you
were about to say what happens when the word is run (which is the
whole dispute).
Here's exactly what you claim happens: If the symbol is undefined at
compile-time, it gets compiled anyhow; in that case, the user/client
is expected to define the word before running it; if they fail to do
so, running the word will produce an error. If they do define the
word, running the word will produce the behavior using the new
definition.
I just realised that you left an ambiguity there, so what I thought
you meant might not be what you actually meant. I thought you meant
that when the word is run, the system looks through the environment to
try to find an apprpriate definition for the word that wasn't known at
compile time. However, you may actually have meant that whenever a
word is defined for the first time, the system searches all words to
try to find any formerly unknown uses of that word, and it permanently
corrects them to match the new definition.
Is that what you meant?
> > > That's because no restrictions are necessary as it's lexically
> scoped
> > > and doesn't allow mutable variables.
> > Non sequitur. Lexical scoping isn't meaningful for your language as
> > you've described it (your language is definitely NOT lexically
> scoped,
> > as I've shown above, since new definitons are NOT available within
> the
> > block in which they lexically appear), and mutable variables have
> > nothing whatsoever to do with the problem.
> Read above.
This paragraph stands on its own. Your language is prima facia not
lexically scoped, because new definitions are not confined to their
lexical context; instead, they follow the dynamic flow of control
wherever it leads.
> > The restrictions that would help would include things like: "dynamic
> > definitions can only be used in runtime code, not inside of a
> > definition." That one requirement would ease all of the
> > restrictions.
> But there are NO dynamic definitions! Bindings occur at run-time but
> they are completely static! You cannot change them once they have
> been created.
You're confusing bindings and definitions. I don't blame you; I did
too, and I had to be corrected.
> > > Bindings can be static and still happen at run-time. Dynamic
> binding
> > > would imply that the binding itself can be changed which is not
> the
> > > case here. A new binding gets created in the environment but all
> the
> > > old bindings, even the ones with the same name are preserved.
> >
> > I apologise; you're right. My confusion, my fault.
> Ah, but you still insist that the language is dynamicly scoped even
> though the bindings are static! Do you not see that all the compiling
> happens at compile time (therefore there is no need for a run-time
> compiler) but all the bindings happen at run-time and are static?
Your bindings are being made at compile-time, not run-time. This is
why they're called static bindings. But your definitions are being
made at runtime; this is why your language has an ordinary library
function, DEF2, which creates a definition at the time it's run. This
is why your definitions are dynamic.
Your language is dynamicly scoped because the scope of the variables
DEF2 creates can only be discovered by discovering the dynamic flow of
control.
> You cannot change existing bindings and you cannot undefine existing
> bindings. You can only create new ones and they only only affect newly
> defined quotations much like binding a name to an expression in a let
> expression will only affect the code that follows the binding.
> How can the language be dynamically scoped then?
These questions are all questions about binding, not scoping. Thus
your last question cannot be answered by referencing any of them.
Oh, by the way, there's no action "undefine an existing binding".
Bindings can't be defined or undefined. Definitions get defined;
bindings get bound.
> > > But the word IS defined everywhere within its lexical scope! I
> think
> > > the problem is that you're still thinking about this as if the
> word
> > > gets defined at compile time - as if the lexical scope of a
> variable
> > > should begin right after DEF or DEF2 has been compiled.
> >
> > Yes, that's the meaning of the word "lexical scope", as opposed to
> > other words like "dynamic scope". Lexical scope means that the word
> is
> > defined over a single, specific area of the source text. Your
> > definitions are dynamicly scoped, and some of the suggestions you've
> > made in an attempt to repair the problem result in something that I
> > could call anti-lexical scoping -- the definitions are dynamicly
> > scoped, but they don't become available until after you've _left_
> the
> > lexical scope in which the definitions are contained.
> No, and I will again point out that compiling quotations/code and
> binding a name are not the same. They are two disctinct operations
> even though most languages don't treat them as such. Binding always
> happens after compiling.
I'm seriously confused why you're saying this. It seems totally
irrelevant to what I'm saying. There also seems to be a confusion --
when you say "binding always happens after compiling" I suspect that
you mean "definition always occurs after compiling".
> > Yes. It's important to notice that a lexically single definition
> could
> > have many scoping regions, and those regions could disappear
> depending
> > on the dynamic specifics of the code executing them. This is why we
> > call it dynamic scoping rather than lexical scoping.
> No - what you are referring to in this case would be conditional
> compilation. I should state that in this case 'compilation' is a bit
> misleading as it's really just conditional binding of names but the
> traditional term is conditional compilation.
Conditional _definition_ of names. AKA dynamic definition.
> Dynamic scoping has nothing to do with that - dynamic scoping would
> mean that once I bind 'a' to say 3, and use it within 'b', and then
> later yet use 'b' within 'c' which redefines 'a' to 5, 'b' would use
> 'a' as if it has been defined as 5 to start with.
No, that's dynamic binding (because already-known bindings in
previously compiled words have to be changed to match new
definitions).
Dynamic scoping is different-- in dynamic scoping there's no guarantee
that you'll be able to access a defined name from any place in the
source text. Instead, the guarantee is that you'll be able to access
the defined name chronologically after the definition has been
executed.
> That's a bit confusing so I'll illustrate that with an example:
>
> [a 3] DEF2
> [b a] DEF2
> [c [a 5] DEF2 b] DEF2
> c b
>
> and on stack you end up with 5 3 - that's what would happen in a
> dynamically scoped language (recall that the scope in dynamically
> scoped languages depends on the nesting of function calls at
> run-time).
> In a statically scoped language I'm trying to describe the above code
> would put 3 3 on stack.
That's static binding, definitely.
If it were static scoping, the DEF2 inside the body of 'c' would only
change the value of 'a' within a staticly fixed area -- usually the
quotation within which the definition appears. Joy is a good example
of this; Joy definitions can be lexically scoped, and when they do so,
you can use them only within the libra for which they're defined.
> dynamically scoped language (recall that the scope in dynamically
> scoped languages depends on the nesting of function calls at
> run-time).
That's a true statement, but painfully inadequate as a definition. The
reason for the unclarity is the statement "the nesting of function
calls at runtime". It's so unclear to you what this means that when
you tried to give me an example, your example contained only one
nested function call (the call from c->b->a), and that nested call
didn't contain any definitions (so it couldn't possibly be a
demonstration of this statement).
The scope of a definition in a dynamicly scoped language begins *when*
a definition is executed. The scope of a definition in a lexically
scoped language begins *where* the definition appears in the source.
See the difference?
> Ivan
-Billy
William Tanksley, Jr — 2005-03-20 05:32:05
John Cowan <
cowan@...> wrote:
> William Tanksley, Jr scripsit:
> > Fascinating -- I don't think I've ever heard the distinction made
> > before. It doesn't seem to me like a really useful distinction,
> > honestly. According to him, "scope" is the lexical area in which the
> > definition is available; but if that's the case, the name "lexical
> > scope" is a redundancy,
> Only in the sense that indefinite scope can be understood as "lexical scope
> extending to the whole of the program".
Um... Why? I mean, I agree totally with that definition of indefinite
scope, but it has absolutely zero to do with what I just said. Right?
I'm at a loss to clarify myself, unfortunately. Obviously the words
that seem so clear to me aren't so clear to you, and probably mean
something entirely different. To begin with, I didn't use the term
"indefinite scope"; I only examined "scope" and "lexical scope". My
point was that if "scope" contains the idea of lexicality in its
definition, then the term "lexical scope" is a redundancy, much like
"lightless darkness".
> > and "dynamic scope" is a contradiction in terms.
> Rather, a shorthand expression for "dynamic extent and indefinite scope".
That's what he claims. I have just attempted to explain why, if I
accept his definition of "scope", it looks to me like a contradition
in terms. Perhaps a better term would be an "oxymoron".
If scope is the _lexical_ area within which a variable can be referred
to, but the variable is dynamicly defined, then there IS no lexical
area within which it can be referred to -- by this definition of scope
(which I find useless) dynamicly defined variables _have_ no scope.
Now, Lisp does things in a way that camoflauges the uselessness of
this definition. In Lisp, every symbol can be used everywhere, which
means that dynamic variables appear to have _infinite_ scope rather
than the zero scope that his strict definition would require.
> > Oh, and he defines "extent" as the time over which a definition
> > is available; but I don't see how that would ever be useful, since
> > there's no such thing as "static extent" in those terms, and dynamic
> > extent is uncomputable.
> Extent can be either dynamic or indefinite ("static extent" indeed makes
> no sense), and we can define indefinite extent as "extent from the dynamic
> start of execution to the dynamic end of execution".
Right; this does make sense. Or even more practically, "extent from
the creation of the thing to the destruction of the last reference to
it." Extent definitely requires a garbage collector or other runtime
mechanism.
> > In summary, I don't think that page really discusses the issue we're
> > discussing; it addresses the specifics of how Lisp works (notice that
> > it claims that dynamic scope is a misnomer simply because Lisp doesn't
> > actually have dynamic scope, not because dynamic scope is impossible),
> No, scope (as defined there) cannot be dynamic, because (as you point out)
> scope has to do with the static program text.
Okay, I think I can read the chapter now and have it make sense. You
have to be careful, though, because the author is not making any
attempts to define any terms; he's only explaining what Lisp does.
This is especially clear when he claims that "dynamic scope is
actually dynamic extent and infinite scope". This is only true for a
language for which ALL symbols are treated as referring to variables
with infinite scope. Without taking that special measure, by his
"definitions" dynamic scope would actually be zero scope.
> "Well, I'm back." --Sam John Cowan <jcowan@...>
-Billy
Ivan Tomac — 2005-03-20 14:33:38
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr"
<wtanksleyjr@g...> wrote:
> > You cannot redefine anything - the language does
> > not allow mutable variables.
>
> Wait, you're now saying that no name in the source can ever be
> assigned to a value once it's been previously assigned? So once I
> assign 3 to the symbol x, x will only and exclusively have the value
> 3, no matter what happens? Nobody else can ever define a variable or
> function using the symbol 'x' ever again?
>
> If this is true, it's the most restrictive proposal I've ever heard.
No because that is exactly how let expressions work. They do not allow
you to change existing bindings - you can only create new bindings.
This is critical information - if you ignored that part then it's no
wonder you're not understanding anything of what I'm saying.
In Caml you can write
let x = 3 ;;
then you can use x in some expression, E, or a function F and then
rebind x afterwards:
let x = 5 ;;
E and F will continue using the old binding. That's because the
previous binding of x did not change - a new binding was created
instead.
In the same way in this imaginary concatenative language you can
write:
[x 3] DEF2
followed by an expression, or a new function definition that uses x,
followed by rebinding of x
[x 5] DEF2
It is exactly the same as in Caml or SML.
Note that in Caml you can't have a let expression that escapes the
scope of it's parent let expression - that is why it's called static
nested scoping - nesting affects scope in Caml but not in all
statically scoped languages.
> Just so we understand: I can't, of course, do that. I never claimed
> to. I claimed that the dynamic (runtime) definitions you were
> proposing would interfere with textual manipulations in the same way
> that mutable variables do.
By saying that you're effectively saying that you can simulate mutable
variables somehow in such a language. How? Although I guess there is
no point in asking at this point considering you misunderstood what I
said about how the language works.
> > But that only affects newly entered definitions, not existing
ones -
> > how can you change the meaning then?
>
> _Textually_, there is no difference between "newly entered" and
> "existing" definitions. If two identical words at the same lexical
> level have different meanings due to dynamic operation of the
> language, the compiler cannot use static analysis.
But two identical words within a quotation have the same meaning - at
compile time the environment is static, I keep saying that over and
over again and you keep ignoring that - all the bindings are known and
it is known that they will not change in the future. New bindings do
not replace old ones - they simply make them inaccessible to the
programmer but not to the quotations that use them.
> You just described conditional compilation. That's dynamic. I show
> below that loops can also dynamicly change the final values bound to
> variables. The compiler can't pretend the definitions are static; in
> static terms, the compiler can't know which definition will be
chosen
> from the IF.
Read below.
> Okay, here's an example.
>
> [x 0] DEF2
> [x [x 1] DEF2]
> [x [x 2] DEF2]
> i swap i
>
> This should produce (0 1) on the stack, unless I got that wrong.
Now,
Actually you did get it wrong - it would produce 0 0 on the stack.
Why? Because both quotations were compiled at the time when the
binding of x was 0.
The following would have produced 0 1:
[x 0] DEF2
[x [x 1] DEF2] i
[x [x 2] DEF2] i
or
[x 0] DEF2
x [x 1] DEF2
x [x 2] DEF2
> by concatenative theory, you should be able to concatenate those two
> quotations together, and get the exact effect of calling them one
> after the other (the swap, of course, just buries the first
function's
> result and gets at the address of the second function, so the
> concatenation shouldn't change the final result):
>
> [x 0] DEF2
> [x [x 1] DEF2 x [x 2] DEF2]
> i
>
> Now the result is (0 0).
>
> The result has changed, but nothing's happened that should be
> violating concatenativity, so the result shouldn't change.
>
You may have a point that if that was rewritten as [x [x 1] DEF2 x [x
2] DEF2] the result would again be 0 0. So maybe it does break
concatenative properties of the language a bit (which would finally
explain why Joy separates defining words from the rest of the
language) but I don't think it matters that much because compile and
run cycles are still separated (although the distinction isn't as
obvious as it is in Joy) - if compile time and run time are looked at
separately then the concatenative properties of the language hold. In
other words:
[x [x 1] DEF2] [x [x 2] DEF2] [i] dip i
is the same as
[x [x 1] DEF2 x [x 2] DEF2] i
but not
[x [x 1] DEF2] i [x [x 2] DEF2] i
because the environment has changed before the 2nd quotation has been
compiled.
> > I said no such thing.
>
> Actually, that's what you said, and what you say below.
>
> > I said that words that have not been defined can be handled in two
> > ways - I said I personally prefer the way where undefined
bindings get
> > compiled in anyway
>
> Okay, and then what happens? "Undefined results"? No, you expect the
> programmer to define the name before the code gets run. You said so
> yourself. And when the code is run, THEN the undefined word will
take
> on the current meaning. Who decides what the current meaning is? Not
> the compiler, since the compiler only knows what was defined at the
> time the word was used. Here's an example:
>
> Assume "print" is defined to print a number. x is not defined.
> [getx x] DEF2
> [x 0] DEF2
> [getx [x getx 1 +] DEF2] 3 times
>
> (I'm not fluent in Joy -- I probably got "times" wrong. Please
assume
> it works the way I wrote it.)
>
> That code will leave (0 1 2) on the stack and x will be 3, if I read
> your proposal right, because the 'x' in 'getx' has to be rebound
when
> it's executed. This has the FULL effect of a mutable variable.
>
No I did not once mention that the binding that was undefined but
still compiled into a quotation would later be redefined. You assumed
it would have been redefined.
For all you know the binding could have remained undefined. And that
is exactly the case - considering how many times I said that existing
bindings cannot change I thought it was obvious that that included
undefined bindings. Think lazy evaluation - you can have infinite
loops, undefined bindings, etc. As long as you never hit that point in
code during execution you won't get any errors. And quotations are
lazy - they delay evaluation.
I have no idea how you got this out of all the stuff I've said so far
especially considering the emphasis I've placed on static bindings and
how the meaning of bindings inside quotations cannot change.
> > I never said anything about dynamic (re)definition of words. In
fact I
> > keep pointing over and over again that you cannot re-define words
at
> > run-time - you can only create new bindings - the existing ones
remain
> > intact. For the purpose of compiling new definitions only the most
> > recent binding is used.
>
> Irrelevant. The old binding is no longer available. Thus, the symbol
> has been redefined. No, that redefinition is not retroactive (as it
> would be with mutable variables or dynamic binding), but its scope
is
> not fixed to any lexical area; its scope depends entirely on how the
> program's execution flows.
>
Oh but the old binding IS available. Just not visible to the
programmer. All the quotations that have been defined before the new
binding was created continue to use the old binding! I keep saying
that over and over again but you keep missing this. It is for this
reason that I keep comparing this to let expressions.
I'm somewhat puzzled as to how you came to that conclusion as pretty
much every example I've provided in the last few posts shows otherwise
- that old bindings are preserved.
Example from one of my past posts (one of few, all of which illustrate
this):
[a 3] DEF2
[b a] DEF2
[a [a 4] DEF2 a] i a b
and on stack you end up with 3 3 4 3
Did you notice that 'b' kept the old binding even after 'a' has been
redefined?
> > No - compilation proceeds one top-level quotation at a time. In
other
>
> Ah, okay. So my second guess was better than my first. Sorry.
>
> > Redefinitions are not redefinitions until run-time (in fact
they're
> > not redefinitions at all - they create new bindings, don't change
> > existing ones).
>
> Pardon, but they ARE redefinitions. They don't change past uses of
the
> bindings, but they DO change the meaning of the text.
>
> > > that rule is antithetical to lexical scoping; in lexical scoping
> > > definitions only affect stuff _contained_ within the
definition's
> > > lexical body, while by this rule definitions only can affect
stuff
> > > not
> > > contained within the lexical body that contains the definition.)
>
> > That's exactly the rule I'm using and no, it does not conflict
with
> > the definition of static scoping (it conflicts with the
definition of
> > static nested scoping however but not static scoping!).
>
> According to FOLDOC, the two mean the same thing. Nested scoping
just
> means that you can redefine a word and when you undefine it the old
> word will still be there. That's pretty much a requirement of
lexical
> scoping; in fact, FOLDOC wasn't able to come up with any examples of
> lexical scoping that weren't also nested lexical.
>
No they do not mean the same thing - nested static scoping is simply
one way to implement static scoping.
Also note that what you've just described is both dynamic and static
nested scoping - to be more specific dynamic scoping also allows you
to redefine a word and when you undefine it the old word will still be
there. One crucial difference that you missed is that in case of
static scoping, the previously defined functions that used the old
binding will keep using the old binding even within the scope of the
new binding while in case of dynamic scoping previously defined
functions will use the new binding within the scope of the new
binding.
The language I've described is an example of a statically scoped
language that is not affected by nesting (the kind of a statically
scoped language that FOLDOC did not have an example for). Read below.
> > As I've already mentioned defining a word is not neccessarily the
same
> > as compiling
>
> I don't know why you mention it; it appears to have no relation to
> anything I've ever said.
>
It has everything to do with what we've been talking about. Scoping in
particular.
There is no reason to assume that just because a binding does not
happen at compile time that it isn't static. We've already agreed that
bindings can be static and still happen at run-time. Dynamic bindings
(as in bindings that change at run-time) are a requirement for dynamic
scoping. In the absence of dynamic bindings the language can be
nothing else but statically scoped.
> > To put it another way - there are only two forms of scoping -
dynamic
> > and lexical/static - if this isn't dynamic scoping then it can
only be
> > static/lexical and it isn't dynamic scoping (hopefully I'll
eventually
> > manage to convice you of that).
>
> I don't see how. It's definitely dynamic, since it happens at
runtime.
> See http://foldoc.hld.c64.org/foldoc.cgi?lexical+scope for
definitions
> that work for me.
>
That's exactly the same definition of lexical/static scoping I've used
few posts ago.
Have a look at the definition of dynamic scoping:
http://foldoc.hld.c64.org/foldoc.cgi?dynamic+scope
Note the part that says "an identifier can be referred to, not only in
the block where it is declared, but also in any function or procedure
called from within that block, even if the called procedure is
declared outside the block". That is not the case here.
> > > [current-item 4] DEF2
> > > [0] [[current-item [current-item 8] DEF2] times] infra
> > > current-item
>
> > > Notice that the definition is now being executed zero times? Now
> > > what
> > > happens? I'd expect [] 4.
>
> > Correct.
>
> But see the problem? The empty list is fine, because it's obviously
> the result of the normal operation of infra and time; but the 4 is
> totally unexpected. The compiler cannot staticly guess that it
should
> be 4. Lexically, there was a definition preceding it that should
have
> changed it to 8; but dynamicly the program chose not to execute that
> definition.
Yes but you're missing the point that only quotations are compiled -
everything else is executed. Therefore compiler only worries about
quotations.
> > Note that that is exactly the same as this:
> > [current-item 4] DEF2
> > [0] [[current-item] times] infra current-item
>
> Except for the lack of any definitions, right? Since that's what
we're
> discussing, I think that's a pretty painful omission.
No, it's exactly the same - when you supplied 0 as an argument to
times whatever was inside the quotation would not have been executed.
DEF2 is just another word that causes a side-effect that affects the
rest of the environment - much like put or print would.
> > while the former can be rewritten as:
> > [current-item 4] DEF2
> > [5] [[current-item] times] infra
> > [current-item 8] DEF2
> > current-item
>
> What if the [5] were dynamicly chosen -- for example, from user
input,
> or read in from a file?
What if a programmer in Joy's repl loop types [4] instead of [5]? Does
it matter? Neither affects anything that has already been written or
typed or compiled or executed or defined - only affects things that
haven't been compiled yet (or in case of a repl loop potentially
things that haven't been evaluated yet). Read bellow.
> > > The problem would be even worse if the
> > > intial definition of current-item were missing, but I'm sure you
> > > expect that.
>
> > > Now what:
>
> > > [current-item 4] DEF2
> > > [5] [[current-item [current-item current-item 1 +] DEF2] times]
> > > infra current-item
>
> > > Interestingly, this produces [4 4 4 4 4] 5, right?
> > >Oh, and also; if
> > > you undefine current-item, you'll find that the old value was 5
as
> > > well, since there are actually 5 new definitions.
>
> You didn't respond to the example, in which the value of
current-item
> mutates to 5.
Sorry about that - yes, [4 4 4 4 4] 5 is correct.
The part about undefining current-item is wrong as there is no way to
undefine it. And it if hasn't been defined initially it'll either
cause an error at compile time (in case undefined bindings are
rejected at compile time) or it will give you an error message when
you try to run the quotation using infra and then another error when
you try to execute current-item afterwards considering that the
quotation failed to execute and create a binding for current-item.
> > > You'd expect this to be identical to the original program,
right?
> > > [current-item 4] DEF2
> > > [[current-item [current-item current-item 1 +] DEF2] times]
> > > [current-item] swap infra
> > > Problem! This isn't the same as the original anymore.
>
> > Problem is that you're assuming compilation happens on a
line-by-line
> > basis. Read above.
>
> My explanation did assume that. But the program as written does show
> what I wanted to show, that a "safe" manipulation is actually
unsafe.
> Exchanging two quotations in the source and then inserting a swap
> should produce the same result; but if the second quotation gets
> executed first and includes a redefinition of a variable in the
first
> quotation, the redefinition changes how the second quotation gets
> compiled.
>
But that is not the case. Read above.
> No, you haven't addressed it; you just claimed I was wrong, and then
> when you tried to explain you stopped the explanation exactly when
you
> were about to say what happens when the word is run (which is the
> whole dispute).
My apologies. I thought it was clear that when I said that bindings
cannot be redefined or undefined that that includes bindings that have
not been defined yet. In other words a binding that has not been
defined and has been compiled into a quotation (assuming it isn't
rejected at compile time) will remain undefined forever.
> Here's exactly what you claim happens: If the symbol is undefined at
> compile-time, it gets compiled anyhow; in that case, the user/client
> is expected to define the word before running it; if they fail to do
> so, running the word will produce an error. If they do define the
> word, running the word will produce the behavior using the new
> definition.
>
> I just realised that you left an ambiguity there, so what I thought
> you meant might not be what you actually meant. I thought you meant
> that when the word is run, the system looks through the environment
to
> try to find an apprpriate definition for the word that wasn't known
at
> compile time. However, you may actually have meant that whenever a
> word is defined for the first time, the system searches all words to
> try to find any formerly unknown uses of that word, and it
permanently
> corrects them to match the new definition.
>
> Is that what you meant?
No, neither is correct.
I'm not sure where you see the ambiguity. I've stated a number of
times that the code or quotation that has already been compiled cannot
see any new bindings - only bindings that were present when the
quotation was compiled.
I've even tried to explain it in a different way - that the state of
the current environment gets associated with a quotation that is
currently being compiled and that that is the environment out of which
that quotation extracts bindings - not some other environment that may
or may not have gained new bindings between the time when the
quotation has been compiled and when the quotation was executed.
Maybe the problem is that it wasn't clear what I meant by bindings.
Ironically I chose to use the term binding names to values and avoided
making references to variables specifically to avoid any ambiguity. In
particular I thought that the references to let expressions would
have at least hinted at what I meant by bindings.
> > > > That's because no restrictions are necessary as it's lexically
> > scoped
> > > > and doesn't allow mutable variables.
>
> > > Non sequitur. Lexical scoping isn't meaningful for your
language as
> > > you've described it (your language is definitely NOT lexically
> > scoped,
> > > as I've shown above, since new definitons are NOT available
within
> > the
> > > block in which they lexically appear), and mutable variables
have
> > > nothing whatsoever to do with the problem.
>
> > Read above.
>
> This paragraph stands on its own. Your language is prima facia not
> lexically scoped, because new definitions are not confined to their
> lexical context; instead, they follow the dynamic flow of control
> wherever it leads.
Alright... another attempt to clarify just how and why this language
is statically scoped and not dynamically scoped. Have a look at the
following quotation:
[[x 4] DEF2]
It does not define anything. It is a list containing another list and
a symbol - on it's own it does nothing. It is not a definition.
Once it's executed however it binds x to 4.
Now this is very important: the only thing that is ever compiled are
quotations! Everything else that occurs at the top-level is executed.
In the context of a repl loop the quotation above is a function that
modifies the environment for all the other quotations that are defined
afterwards.
You can do exactly the same thing using let expressions in Caml or
SML. In Caml let expression takes two forms and the second one is just
a special case of the first one:
1. let x = 3 in x ;;
2. let x = 3;; x ;;
The scope of the second one encompasses EVERYTHING that follows in the
source text up until the end of input or the point where x is bound to
another value while the scope of the first one extends only until ;;
The only difference is in how Caml handles nesting. The scope of a
nested let expression in Caml exists only within the scope of it's
parent let expression.
In the hypothetical concatenative language I've described that is not
the case - nested definitions, if executed, will create permanent
bindings of which the scope extends all the way to either the end of
input or the point where a new binding with the same name is created.
In the context of an external compiler think meta-programming.
Quotations are compiled, everything else that occurs at the top level
is executed. Finally you could bind a symbol such as main to a
quotation to make that an entry point for the final program.
Even if main (or any other words it calls) does contain any DEF2s
inside, in this context they are completely meaningless and the
compiler can simply treat them as dead code as new bindings do not
affect existing quotations and upon the completion of main's execution
the control returns to the operating system.
> > > The restrictions that would help would include things like:
"dynamic
> > > definitions can only be used in runtime code, not inside of a
> > > definition." That one requirement would ease all of the
> > > restrictions.
>
> > But there are NO dynamic definitions! Bindings occur at run-time
but
> > they are completely static! You cannot change them once they have
> > been created.
>
> You're confusing bindings and definitions. I don't blame you; I did
> too, and I had to be corrected.
>
> > > > Bindings can be static and still happen at run-time. Dynamic
> > binding
> > > > would imply that the binding itself can be changed which is
not
> > the
> > > > case here. A new binding gets created in the environment but
all
> > the
> > > > old bindings, even the ones with the same name are preserved.
> > >
> > > I apologise; you're right. My confusion, my fault.
>
> > Ah, but you still insist that the language is dynamicly scoped
even
> > though the bindings are static! Do you not see that all the
compiling
> > happens at compile time (therefore there is no need for a run-time
> > compiler) but all the bindings happen at run-time and are static?
>
> Your bindings are being made at compile-time, not run-time. This is
> why they're called static bindings. But your definitions are being
> made at runtime; this is why your language has an ordinary library
> function, DEF2, which creates a definition at the time it's run.
This
> is why your definitions are dynamic.
>
> Your language is dynamicly scoped because the scope of the variables
> DEF2 creates can only be discovered by discovering the dynamic flow
of
> control.
>
I'm confused - what do you consider a definition and what a binding?
Bindings occur at run-time. Defining new words occurs at run-time. The
only thing that that happens at compile time is compiling quotations.
> > You cannot change existing bindings and you cannot undefine
existing
> > bindings. You can only create new ones and they only only affect
newly
> > defined quotations much like binding a name to an expression in a
let
> > expression will only affect the code that follows the binding.
> > How can the language be dynamically scoped then?
>
> These questions are all questions about binding, not scoping. Thus
> your last question cannot be answered by referencing any of them.
Actually they are about scoping because scoping affects binding and
binding depends on scoping. I thought the example at the end of my
last post illustrated that. If not then here's a link to another
example:
http://www.n-a-n-o.com/lisp/cmucl-tutoria
ls/LISP-tutorial-12.html
> Oh, by the way, there's no action "undefine an existing binding".
> Bindings can't be defined or undefined. Definitions get defined;
> bindings get bound.
What do you consider a definition? To me a definition is a name bound
to a value.
> > No, and I will again point out that compiling quotations/code and
> > binding a name are not the same. They are two disctinct operations
> > even though most languages don't treat them as such. Binding
always
> > happens after compiling.
>
> I'm seriously confused why you're saying this. It seems totally
> irrelevant to what I'm saying. There also seems to be a confusion --
> when you say "binding always happens after compiling" I suspect that
> you mean "definition always occurs after compiling".
>
It isn't irrelevant. You said that definitions do not become available
until after they've left the lexical scope which is not true. As I've
already explained above [[x 4] DEF2] does nothing. The definition
inside does not occur until you execute the quotation. The point in
the code where you execute the quotation is the beginning of the
lexical scope of the definition.
> > No - what you are referring to in this case would be conditional
> > compilation. I should state that in this case 'compilation' is a
bit
> > misleading as it's really just conditional binding of names but
the
> > traditional term is conditional compilation.
>
> Conditional _definition_ of names. AKA dynamic definition.
>
I don't think that's the case - I think you're limiting the definition
of static scoping to static nested scoping.
> > Dynamic scoping has nothing to do with that - dynamic scoping
would
> > mean that once I bind 'a' to say 3, and use it within 'b', and
then
> > later yet use 'b' within 'c' which redefines 'a' to 5, 'b' would
use
> > 'a' as if it has been defined as 5 to start with.
>
> No, that's dynamic binding (because already-known bindings in
> previously compiled words have to be changed to match new
> definitions).
>
> Dynamic scoping is different-- in dynamic scoping there's no
guarantee
> that you'll be able to access a defined name from any place in the
> source text. Instead, the guarantee is that you'll be able to access
> the defined name chronologically after the definition has been
> executed.
>
I will again point you at
http://www.n-a-n-o.com/lisp/cmucl-tutorials/LISP-tutorial-12.html
Also have a look at how dynamic scoping is defined at FOLDOC.
Especially the part that I've already mentioned in this post and that
states "an identifier can be referred to, not only in the block where
it is declared, but also in any function or procedure called from
within that block, even if the called procedure is declared outside
the block".
> > That's a bit confusing so I'll illustrate that with an example:
> >
> > [a 3] DEF2
> > [b a] DEF2
> > [c [a 5] DEF2 b] DEF2
> > c b
> >
> > and on stack you end up with 5 3 - that's what would happen in a
> > dynamically scoped language (recall that the scope in dynamically
> > scoped languages depends on the nesting of function calls at
> > run-time).
> > In a statically scoped language I'm trying to describe the above
code
> > would put 3 3 on stack.
>
> That's static binding, definitely.
>
> If it were static scoping, the DEF2 inside the body of 'c' would
only
> change the value of 'a' within a staticly fixed area -- usually the
> quotation within which the definition appears. Joy is a good example
> of this; Joy definitions can be lexically scoped, and when they do
so,
> you can use them only within the libra for which they're defined.
Ah again - that is true for static nested scoping, not necessarily
static scoping!
> > dynamically scoped language (recall that the scope in dynamically
> > scoped languages depends on the nesting of function calls at
> > run-time).
>
> That's a true statement, but painfully inadequate as a definition.
The
> reason for the unclarity is the statement "the nesting of function
> calls at runtime". It's so unclear to you what this means that when
> you tried to give me an example, your example contained only one
> nested function call (the call from c->b->a), and that nested call
> didn't contain any definitions (so it couldn't possibly be a
> demonstration of this statement).
How is it not a demonstration of the statement?
'a' is first defined as 3.
'b' is defined as 'a'.
At run-time 'c' redefines 'a' as 5 and calls 'b'.
When 'c' terminates the previous definition of 'a' is restored and 'b'
continues using that definition.
So the execution of 'b' varies depending on whether it's nested inside
of 'c' or whether it's called from outside of 'c'.
> The scope of a definition in a dynamicly scoped language begins
*when*
> a definition is executed. The scope of a definition in a lexically
> scoped language begins *where* the definition appears in the source.
> See the difference?
I don't know if I agree with that. In a dynamically scoped language
when the definition is executed it changes existing bindings - in a
statically scoped language it does not (your definition of static
scoping only includes nested static scoping).
> -Billy
Ivan
sa@dfa.com — 2005-03-24 17:04:21
compute the partitions of an integer. e.g.
5 partition
[[5]
[4 1]
[3 2]
[3 1 1]
[2 2 1]
[2 1 1 1]
[1 1 1 1 1]]
test:
20 partition length
627
phimvt@lurac.latrobe.edu.au — 2005-04-04 07:12:09
On Thu, 24 Mar 2005
sa@... wrote:
> compute the partitions of an integer. e.g.
[..]
Nice problem. I immediately wrote a Joy program which would have
run, but even before I got to type it in I recognised that it
would have produced the wrong output. So I started again from
scratch - trying to understand what the problem really was.
This is how far I got:
I initially conceived of the problem as involving induction
of some sort: from the answers to (n), develop the answers
to (n+1). It looked rather like Pascal's triangle at first,
but later I saw that we need a sort of stack of triangles,
written, if you like, on sheets of acetate film. The first
three triangles are shown below. I recognised the need for
further triangles from the fact that the first triangle was
incomplete in row 4, and the need for a third triangle comes
in row 6. The notation is important: 3211 means one 3, one 2,
two 1's. Note THE PATTERN: going diagonally down to the
left, we increment the first number, going diagonally down
to the right we append a 1.
n: f i r s t s h e e t second sheet third
1: 1
2: 2 11
3: 3 21 111
4: 4 31 211 1111 22
5: 5 41 311 2111 11111 32 221
6: 6 51 411 3111 21111 111111 42 321 2211 33
This suggests that for even n we must start a new sheet with a new
triangle with the apex of the form nn. I have no way of knowing whether
this will complete all rows (or are they really triangles stand up).
I cannot spend more time on this. And I won't try to write a Joy
program until I know that I have the pattern right.
How am I doing, oh Master?
I am now going to google for help.
- Manfred
Michael Pruemm — 2005-04-04 08:17:06
On Apr 4, 2005 9:12 AM,
phimvt@...
<
phimvt@...> wrote:
> 6: 6 51 411 3111 21111 111111 42 321 2211 33
And don't forget
6: ... 222
This would be another sheet in your pattern.
- Michael
sa@dfa.com — 2005-04-04 13:37:48
phimvt@... wrote on 04/04/2005 03:12:09 AM:
>
>
> On Thu, 24 Mar 2005 sa@... wrote:
>
> > compute the partitions of an integer. e.g.
>
> [..]
>
> Nice problem. I immediately wrote a Joy program which would have
> run, but even before I got to type it in I recognised that it
> would have produced the wrong output. So I started again from
> scratch - trying to understand what the problem really was.
> This is how far I got:
>
> I initially conceived of the problem as involving induction
> of some sort: from the answers to (n), develop the answers
> to (n+1).
that's also the way i started thinking about it. here's
the algorithm i settled on (with some help from a collection
of combinatorial routines written in fortran):
the algorithm starts with the unit list [n] and ends with
a list of n 1s. e.g. [7] -> [1 1 1 1 1 1 1]. given a
list v, the algorithm produces its successor. for example,
suppose in the course of generating the partitions of 7,
you have v
[3 2 1 1]
compute the successor list p^d^r as follows:
i = the index of the smallest part > 1
k = that part minus 1
p = everything up to but not including that part
s = the sum of everything following and including that part
m = s mod k
j = s / k (i.e. integer division of s by k)
r = the remainder of [m] or [] if m is 0
d = a list of j-many copies of k
return p^d^r
so the successor of v is:
i = 1
k = 1 = 2-1
p = [3]
s = 4
m = 0
j = 4
r = []
d = [1 1 1 1]
return [3]^[1 1 1 1]^[] = [3 1 1 1 1]
my k implementation (i haven't attempted to write one in a
concatenative language) is:
part:{[v]
if[0>i:-1+(v>1)?0;:v] / index of smallest part>1
k:-1+v i / that part-1
p:i#v / prefix
s:+/i _ v / sum of suffix
if[~r:s!k;r:()] / remainder if not zero
d:(_ s%k)#k / distribute components
p,d,r}\,: / all
e.g.
part 7
(,7
6 1
5 2
5 1 1
4 3
4 2 1
4 1 1 1
3 3 1
3 2 2
3 2 1 1
3 1 1 1 1
2 2 2 1
2 2 1 1 1
2 1 1 1 1 1
1 1 1 1 1 1 1)
hardly the most intuitive algorithm i've ever seen! your
recursive-triangles approach seems more promising, at least
as a way of producing that sense "inner conviction" of
the method's correctness.
It looked rather like Pascal's triangle at first,
> but later I saw that we need a sort of stack of triangles,
> written, if you like, on sheets of acetate film. The first
> three triangles are shown below. I recognised the need for
> further triangles from the fact that the first triangle was
> incomplete in row 4, and the need for a third triangle comes
> in row 6. The notation is important: 3211 means one 3, one 2,
> two 1's. Note THE PATTERN: going diagonally down to the
> left, we increment the first number, going diagonally down
> to the right we append a 1.
>
> n: f i r s t s h e e t second sheet third
>
> 1: 1
>
> 2: 2 11
>
> 3: 3 21 111
>
> 4: 4 31 211 1111 22
>
> 5: 5 41 311 2111 11111 32 221
>
> 6: 6 51 411 3111 21111 111111 42 321 2211 33
>
> This suggests that for even n we must start a new sheet with a new
> triangle with the apex of the form nn. I have no way of knowing whether
> this will complete all rows (or are they really triangles stand up).
> I cannot spend more time on this. And I won't try to write a Joy
> program until I know that I have the pattern right.
>
> How am I doing, oh Master?
>
> I am now going to google for help.
>
> - Manfred
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
janvanlent — 2005-04-04 14:40:38
icpdesign — 2005-09-20 20:39:01
I am ventoring here, i do not have sound theroetical knowledge about language models.
But, it seems to me that languages with dynamic scoping are very difficult to implement
with parallel constructs.
If at one point in time there is 2 concurrent threads running and both depends on a
defined name n, and if one of the thread redefines the name n than what happens in the
other running thread? what value of n it should use. You may find a protocol to this, but
then compare this with sequential running; in sequential mode it is sound to think that as
soon as you redefine the name n, then all subsequent quotations see the new value of the
name n.
So dynamically scoped languages are non-determinist; since the result of redefining
names may give different results in different runs with the same input.
A picture, because i know i am most of the time i am not precise in my writing:
DEFINE
a == 1.
in Parallel:
scenario 1:
-----------------------------------------------------------> time
... [................... [a 2] DEF ...] # thead 1
[.... a ...] ... # thread 2
^ here a = 1
scenario 2:
-----------------------------------------------------------> time
... [... [a 2] DEF ...] # thead 1
[....................... a ...] ... # thread 2
^ here a = 1
in Sequence
... [... [a 2] DEF ...] [... a ...] ...
^here a should be seen as equal to 2
comments are very welcome
Taoufik Dachraoui
--- In concatenative@yahoogroups.com, "William Tanksley, Jr" <wtanksleyjr@g...> wrote:
> Ivan Tomac <e1_t@y...> wrote:
> > For some strange reason some of the recent posts to this message board made
> > me dig through the archives and re-read some of the old posts as well as go
> > through most of the papers on Joy again.
>
> Hey, I did that too :-). It's almost worth being proven wrong to see
> the traffic on the list pick up :-).
>
> > (http://www.latrobe.edu.au/philosophy/phimvt/joy/faq1.html) Manfred talks about
the
> > reasons why having defining words avaliable at run-time is a bad idea. The
> > reason he states in the end is one relating to creation of variables that act the
> > same way as they would in imperative languages.
>
> That's not quite what he says -- he says that such definitions would
> not act in a way he considers appropriate to functional languages.
>
> The reason that functional languages don't allow mutable variables is
> because the variables in lambdas aren't mutable; thus, the languages
> modify what they can do in order to more closely conform to their
> models. Definitions in Joy are similar to variables in lambda
> expressions.
>
> This is a reasonable choice, but not the only one.
>
> > I have one question and one (obvious) observation:
> > 1) Wouldn't this only be a problem with dynamically scoped languages? If Joy
> > was lexically scoped this wouldn't be an issue.
>
> Er... True, but not for the reason you're stating. A lexically scoped
> language cannot possibly have defining words available at runtime, so
> no issue. Lexical scope means that definition availability is
> determined only by the static source text. If defining words were
> available at runtime, we'd have dynamic scoping.
>
> With that said, there's nothing bad about dynamic scope and allowing
> redefinitions.
>
> > Ivan
>
> -Billy
icpdesign — 2005-09-20 21:23:02
< i subsmit the message again because of the problem in the
formating, sorry>
I am ventoring here, i do not have sound theroetical knowledge
about language models.
But, it seems to me that languages with dynamic scoping are
very difficult to implement with parallel constructs.
If at one point in time there is 2 concurrent threads running
and both depends on a defined name n, and if one of the thread
redefines the name n than what happens in the other running
thread? what value of n it should use. You may find a protocol
to this, but then compare this with sequential running; in
sequential mode it is sound to think that as soon as you
redefine the name n, then all subsequent quotations see the
new value of the name n.
So dynamically scoped languages are non-determinist; since the result of
redefining
names may give different results in different runs with the same input.
A picture, because i know i am most of the time i am not precise in my writing:
DEFINE
a == 1.
in Parallel:
scenario 1:
---------------------------------------> time
... [.......... [a 2] DEF ...] ---# thead 1
[.... a ...] ... -----------------# thread 2
------^ here a = 1
scenario 2:
-------------------------------------------> time
... [... [a 2] DEF ...] ------------# thead 1
[.................. a ...] -------- #thread 2
--------------------^ here a = 2 or 1
in Sequence
... [... [a 2] DEF ...] [... a ...] ...
-----------------------------^ here a should be seen
----------------------------- as equal to 2
Note: In the parallel scenario i assumed that the 2 quotations
can run in parallel
comments are very welcome
Taoufik Dachraoui
> --- In concatenative@yahoogroups.com, "William Tanksley, Jr" <wtanksleyjr@g...>
wrote:
> > Ivan Tomac <e1_t@y...> wrote:
> > > For some strange reason some of the recent posts to this message board made
> > > me dig through the archives and re-read some of the old posts as well as go
> > > through most of the papers on Joy again.
> >
> > Hey, I did that too :-). It's almost worth being proven wrong to see
> > the traffic on the list pick up :-).
> >
> > > (http://www.latrobe.edu.au/philosophy/phimvt/joy/faq1.html) Manfred talks about
> the
> > > reasons why having defining words avaliable at run-time is a bad idea. The
> > > reason he states in the end is one relating to creation of variables that act the
> > > same way as they would in imperative languages.
> >
> > That's not quite what he says -- he says that such definitions would
> > not act in a way he considers appropriate to functional languages.
> >
> > The reason that functional languages don't allow mutable variables is
> > because the variables in lambdas aren't mutable; thus, the languages
> > modify what they can do in order to more closely conform to their
> > models. Definitions in Joy are similar to variables in lambda
> > expressions.
> >
> > This is a reasonable choice, but not the only one.
> >
> > > I have one question and one (obvious) observation:
> > > 1) Wouldn't this only be a problem with dynamically scoped languages? If Joy
> > > was lexically scoped this wouldn't be an issue.
> >
> > Er... True, but not for the reason you're stating. A lexically scoped
> > language cannot possibly have defining words available at runtime, so
> > no issue. Lexical scope means that definition availability is
> > determined only by the static source text. If defining words were
> > available at runtime, we'd have dynamic scoping.
> >
> > With that said, there's nothing bad about dynamic scope and allowing
> > redefinitions.
> >
> > > Ivan
> >
> > -Billy