|
|
Home
>
3. concept
>
3.1 binding
|
Previous
Next
|
|
|
|
|
|
|
A binding is an association of a name to a value within a module. Both variable
and
constant bindings can be defined.
|
|
fun-
principal
|
In a define
|
|
Chris
Page
|
"constant" is probably a bad choice of terms.
They're really
just
"read-only variables". A module constant
can change between
runs of a
program, since they can be initialized from arbitrary
code.
For
example, a constant could contain the IP address of
the
computer it's
running on.
|
|
|
|
|
|
|
|
|
|
> But what do you mean above by a binding?
A binding is a name in some namespace, bound to a value.
For example,
the name of every generic function, module variable,
and local variable
is a "binding".
>>> list is not taken into account for selecting
the correct procedure.
>>> Even Langauges like
>>> Ada 95 are able to do this!
>>
>> Again, functions are just bare methods that
happen to be named. GFs
>> are a different kind of object, that is callable
too, but does
method
>> selection.
>
> This is what i mean by a flaw. I can't understand
why this distinction
> is made?!
Note that when you define a method, it doesn't get
a binding. Instead,
only the generic function has a name associated with
it. The methods
are referred to by the generic function.
In Ada or C++ you can use "overloading" to
have multiple functions with
different signatures, and the compiler treats each
one as a separate
function. Since they are statically-typed languages,
the compiler
always knows the exact types of the arguments at every
call site, which
means it can always decide which function to call at
compile-time based
upon the argument types.
In addition, each of those functions can only handle
arguments of one
exact type. A C++ function that accepts int cannot
also accept a float
in the same argument position.
In contrast, Dylan supports dynamic programming, where
the exact types
of all the arguments at a given call site may not be
known. In fact,
the types don't have to be known at all for the program
to compile and
run correctly.
At runtime, argument types are used to select which
method of a generic
function to call. This provides a similar behavior
to "overloading",
except that the types don't have to be known at compile
time. In fact,
the set of methods don't have to be known at compile
time, either. The
set of methods in a generic function can be changed
at runtime.
Since the compiler may not be able to determine which
method to call,
the only information it can always rely upon to compile
a function call
is the name of the generic function being called.
In Dylan, the name of a generic function identifies
a "family" of
methods, and the generic function must have a unique
name. In a way,
it's just a variation of "overloading" in
C++, but it's more flexible
and powerful. Every generic function is like a set
of overloaded,
non-member functions, and every method is like a virtual
member
function in that you can have multiple methods handling
different
subclasses of arguments.
By the way, if multiple generic functions had the same
name, Dylan
would have to have yet another level of dispatching
added to determine
which generic to call, and then determine which method
to call. This
may or may not be a good idea, but Dylan doesn't have
a way to do that,
currently. Instead, each generic is uniquely identified
by its name and
function calls using that name can be successfully
compiled.
|
|
Is dynamic binding a key aspect?
example is it possible to extend
data Pet = Cat | Dog
data Domestic = Pet | |Cow | Horse
is qouted_var = '{ x * y + z }
applied quoted_var ( x = 2, y = 4, z = 6 )
|
|
|
|
|
|
|
Dylan won't let one name have different types of bindings in a
single context. That is, a name cannot refer to a generic
function *and* some other kind of value.
[ In Dylan a namespace for bindings (the context of a binding) is
the module.]
|
|
|
|
Johan Ovlinger
|
I assume that there are scoping constructs so that two different method using the
same naming don't interfere with each other?
|
|
Scott McKay
|
Of course, this has all been done in Lisp (CLOS) and Dylan. Generic functions are
what you use to extend functionality, packages (or modules) are what you use to
prevent clashes.
|
|
|
|
Jonathan
Bachrach
|
In Dylan you can't create bindings at runtime, so, in the very least you
must create a bunch of variables for your accessors up front.
If you don't create these bindings up front, then calls to accessors
corresponding to these bindings would give rise to undefined binding
errors when compiled.
|
|
|
|
|
|
|
|
|
|
Bruce Hoult
|
what's the inverse of 'define variable ' and 'define method' ?
i.e.
?
foo;
error: unbound variable: foo.
** Debugger **
Debug[1]>
[interrupt -- returning to top level.]
? define variable foo = 99;
? foo;
==> 99
?
WHATGOESHERE??
? foo;
error: unbound variable: foo.
** Debugger **
|
|
Seth
LaForge
|
The point of define at the top level is that it creates a global binding.
There are two things you can do to get the effect you want:
- define the variable in it's own module
(this prevents it from being
visible elsewhere, unless you specifically export it; or
- create a local binding using let.
Here's an example of the latter:
?
foo;
error: unbound variable: foo.
? begin
> let foo = 99;
> foo;
> end;
==> 99
? foo;
error: unbound variable: foo.
?
|
|
|
|
|
|
|
Brandon Corfman
|
Although I've gotten a few comments from people pointing out that you won't
get a compiler error in C++ from the following:
void main()
// OK, this a really silly example ...
// but it illustrates my point ...
char stuff[] = "Brandon";
while (1)
{
char stuff[] = "Corfman";
}
}
You will get an error from this:
void main()
char stuff[] = "Brandon";
char stuff[] = "Corfman";
at least I do under VC++.
But I won't get a compiler error with Dylan if I do this, but I think it would be
certainly nice if I did:
define method main () => ()
let answer :: <string> = "Brandon";
let answer :: <string> = "Corfman";
end method main;
So I think my point is still valid.
|
|
|
No Becauce the equivalent C++ for the above Dylan code is
void main ()
{
char stuff[] = "Brandon";
{ char stuff[] = "Corfman";
}
}
Which again will NOT issue any warning at all.
Every time you see the word 'let' a NEW lexical context created. If yo do
not want a new lexical context, don't use the word.
To bind variable to a new value you can use a -setter method or the
syntactic sugar equivalent, ':=' .
[ Personally, I think the word "assignment" carries too much baggage
for C/Algol folks. The expectation is the "assignment" makes a
copy. For the most part, no "copy" is created in Dylan unless you
explicitly ask for one. ]
If you want to keep this straight then I'd suggest a slightly different coding
style.
define method main () =>
()
let answer :: <string> = "Brandon";
let answer :: <string> = "Corfman";
end method main;
Namely, indent for each new lexical scope you create.
[ I think some folks avoid this because they keep the "rule" straight
in their head and want to avoid the code "drifting to the right".
This has a negative impact on "newbies" though. ]
Jason Trenouth wrote
> I think a Dylan implementation is free to warn you about
> the circumstance you describe, its just the current
> implementations don't. ie you keep saying
There is nothing wrong. It could be "style" warning is you had some Dylan
style checker. ( where the style violations is either indentation or "reuse"
depending on your preference) However, semantically if you would "warn"
for this you should also "warn" if a function/method parameter is the same
name as one in the outer scope. The two have effectively the same
signficance. If don't want one it seem "hypocritical" to want the other.
Now this would be more blanantly obvious if there were either a requirement
for an explicit 'end' for each 'let' or Dylan still had the prefix syntax.
let answer ....
let answer ...
end let
end let
or
(let ...
(let ...
)
)
However, the current system has the benefit of cutting down on the number
of "ends" you have to insert into your code. Very often you would end up
with sequence of 'end's. The current systax manages to "reuse" the 'end'
to close multiple "enclosures". This "special reuse" is only "combines"
'let'
with others (or itself) so the meaning is straightforward once you know the
rule.
|
|
|
|
Marco G.
Antoniotti
|
The 'let' *is* allowed to float around in Dylan programs. As minor
as it is, this really bothers me.
The point of having 'let' as syntactic sugar for a functional binding
is somewhat lost in Dylan. A syntax like ML
let <bindings>
in
<ops>
end
would have made more sense.
|
|
Paul Haahr
|
Which, in Dylan, would probably be transliterated as something
like
let (bindings) body ... end
given the rest of the syntax.)
I have to admit that this bothered me, too, at first. Until I started
using it. All of a sudden, I discovered the joy of not needing a
new level of indentation just to introduce a new local variable.
(C++ programmers probably feel the same way: C requires that
local declarations appear at the beginning of a {} enclosed block,
where C++ allows them in any statement position. Very
convenient.)
But, if you really don't like that bit of Dylan syntax after trying it,
here's a ``bind'' macro which behaves like a Lisp/Scheme let*.
The word ``let'' is one of the eight or so truly reserved words in
Dylan that can't be renamed, so I had to use a different name.
define macro bind
{ bind (?bindings) ?:body end }
=> { ?bindings; ?body }
bindings:
{ ?binding:*, ... }
=> { let ?binding; ... }
{ }
=> { }
end macro;
So, for example,
bind (a = 1, b = 2)
a + b
end
=> 3
bind (a = 1, b = a + 1)
b
end
=> 2
bind (a
= 13, a = 42)
a
end
=> 42
|
|
Marco G.
Antoniotti
|
The problem with the choice made by the Dylan designers is that
we'll see a lot of code written like
<statements>*
let newvar = 33;
<statements>*
let anothervar = 44;
<statements>*
|
|
Paul Haahr
|
I expect so, too. I've written lots of code like that myself. I think
it's perfectly readable, maintainable code. I like it. What about it
do you find problematic
|
|
Marco G.
Antoniotti
|
Not Lispish enough :)
|
|
Enrico
Colombini
|
> From: Paul Haahr:
> I expect so, too. I've written lots of code like that
> myself. I think it's perfectly readable, maintainable code.
> I like it.
I like it too, and consider it a major readability improvement from
Lisp (no holy wars, please).
|
|
Jeff Dalton
|
Look at it like this: Dylan-style "let" can more naturally replace
assignment in more cases. And Dylan is trying to attract people
away from languages where they'd be using assignment all over
the place.
(I know there are arguments that point the other way. E.g. better
attract them to a more functional style so that it's less likely that
bad habits will be carried over. But, on balance, I think Dylan's
"let" is at least a reasonable idea and probably a good one.)
> <statements>*
> let newvar = 33;
> <statements>*
> let anothervar = 44;
> <statements>*
FWIF, I prefer code like that above to code w/ a single "let" at the
top but with the other "lets" replaced by assignments.
|
|
|
|
Jeff Dalton
|
Dylan's interpretation of "let" solves a problem that can be
significant in some other languages (such as Common Lisp,
Scheme, and some functional languages), namely that
introducing new variables also means increasing the
indentation. Dylan also eliminates the problem in Common
Lisp that let and multiple-value-bind are two separate forms,
so that it's awkward to deal with multiple values together
with other cases (though in CL one can, of course, write a
macro).
I also think Dylan does a pretty good job of reducing the
potential of equality / assignment confusion by using := for
assignment and by having a keyword ("let") when = is used
when introducing a variable.
There can be a period when learning a new language when
certain mistakes seem to be often in the way. I think the
right test here is whether programmers continue to be
tripped up when they become more experienced. I suspect,
though I can't prove it, that most programmers will end up
finding Dylan's "let" to be pretty reasonable, or even rather
nice.
The use of "let" for assignment in some varieties of Basic is
very unusual when we consider the world of programming
languages as a whole. "let" almost always has a
declarative, variable- introduction, function, rather than being
a way to assign to an existing variable. Even some Basics
have adopted "set" for assignment, in recognition of this.
Indeed, as someone who has written a fair amount of Basic
in a dialect that used "let" for assignment (though this was
years ago), I quite like Dylan's "let".
There are some things about Dylan's syntax that I don't like,
but "let" isn't one of them.
|
|
|
|
|
|
|
Brandon
Corfman
|
I'm was busy developing a console app in Dylan when I mistakenly wrote this
piece of code:
format-out("Do you want to play again? ");
let answer :: <string> = read-line(*standard-input*);
while (answer = "")
format-out("Do you want to play again? ");
let answer :: <string>
= read-line(*standard-input*);
end while;
The strange thing is, Dylan let me redefine answer without throwing an error.
Then, when the program runs, the first read-line works correctly, but if I hit
Enter so that the program drops into the while loop, it never comes out again.
It seems that the while condition is actually looking at the _original_ answer
variable and not the newly redefined answer variable.
I don't understand this behavior, being new to Dylan. Can someone explain it
to me?
|
|
Gabor Greif
|
You cannot assign a new value to an existing binding (variable) with let. Let
always creates a new binding in the smallest enclosing scope (here the while
body). This is the same in C++:
int main()
string answer;
cin >> answer;
while (answer == "")
{
string answer;
cin >> answer;
} // 2nd answer is destructed here...
If you want to assign to an existing binding, use the assignment operator:=
But I guess your problem is nicer solved by using tail recursion, thus not
needing assignment at all:
begin
local method
play(prompt :: <string>,
action :: <function>)
format-out(prompt);
let answer :: <string>
= read-line(*standard-input*);
if (answer = "")
action();
play(prompt, action);
end
end;
play("Do you want to play again?\n",
method()
format-out("playing...\n");
end)
end
(code not checked)
|
|
Hugh G.
Greene
|
To quote the DRM (Chapter 14, Table 14-2):
let Creates
and initializes new local bindings
within the scope of the smallest enclosing
implicit body.
(and from the description below)
The bindings are visible for the remainder
of the
[body].
An "implicit body" includes the inside of "define method" and
of "statement
macros" like "while", "if", "for" etc. The macro "let"
creates new bindings
(between names and values, or "locations for values") which last for the rest
of the body. It's valid to re-bind a name, in the same or a narrower scope,
though you might want a warning from a compiler if you did it in the same
scope.
In your code, the first binding of "answer" is in scope in the test clause
of the
while loop and in the body of the while loop, right up until after the second
binding of "answer" (which shadows the first) and again after the while loop
(because that's outside the implicit body where the second binding occurs).
What you want to do would be written like this:
format-out("Do you want to play again? ");
let answer :: <string> = read-line(*standard-input*);
while (answer = "")
format-out("Do you want to play again? ");
answer := read-line(*standard-input*);
end while;
where ":=" changes the value bound to the name "answer", rather
than
creating a new binding (with a separate "location").
Or, you could abbreviate it like this:
local method ask () => (answer :: <string>)
format-out("Do you want to play again? ");
read-line(*standard-input*)
end;
let answer :: <string> = "";
while ((answer := ask()) = "")
end;
Does that help?
|
|
|
|
Jason Pellerin
|
I still haven't made the mental leap Jasonfrom variables-in-c to variables- in-
dylan.
I probably shouldn't be thinking in terms of variables at all, but in terms of
objects, with a variable just being the name of some object
|
|
Mark Craig Chu-
Carroll
|
Variables are even weaker than that in Dylan.
A variable is a *temporary* name for an object. "let a = make(<gobble>)"
doesn't establish "a" as the name for the newly created object; it establishes
a temporary binding from the name "a" to the newly created object.
Reassigning a new value to the variable doesn't affect the old value in any
way.
|
|
|
|
|
define method main () => ()
let answer :: <string> = "Brandon";
let answer :: <string> = "Corfman";
end method main;
But I won't get a compiler error with Dylan if I do this, but I think it would be
certainly nice if I did:
|
|
|
No Becauce the equivalent C++ for the above Dylan code is
void main ()
{
char stuff[] = "Brandon";
{ char stuff[] = "Corfman";
}
}
The following fits into the same category.
let index = 23
;
for ( index from 0 to 10 )
a[index] := index * 23;
end for;
// index equals ??
After the loop, 'index' is 23. The loop's 'index' masks
the "outer" let.
|
|
Hugh G.
Greene
|
To quote the DRM (Chapter 14, Table 14-2):
let
Creates and initializes new local bindings
within the scope of the smallest
enclosing
implicit body.
(and from the description below)
The bindings are visible for the remainder
of the
[body].
An "implicit body" includes the inside of "define method" and
of "statement
macros" like "while", "if", "for" etc. The macro "let"
creates new bindings
(between names and values, or "locations for values") which last for the rest
of the body. It's valid to re-bind a name, in the same or a narrower scope,
though you might want a warning from a compiler if you did it in the same
scope.
In your code, the first binding of "answer" is in scope in the test clause
of the
while loop and in the body of the while loop, right up until after the second
binding of "answer" (which shadows the first) and again after the while loop
(because that's outside the implicit body where the second binding occurs).
What you want to do would be written like this:
format-out("Do you want to play again? ");
let answer :: <string> = read-line(*standard-input*);
while (answer = "")
format-out("Do you want to play again? ");
answer := read-line(*standard-input*);
end while;
where ":=" changes the value bound to the name "answer", rather
than
creating a new binding (with a separate "location").
Or, you could abbreviate it like this:
local method ask () => (answer :: <string>)
format-out("Do you want to play again? ");
read-line(*standard-input*)
end;
let answer :: <string> = "";
while ((answer := ask()) = "")
end;
Does that help?
|
|
|
|
|
|
|
Neelakantan
Krishnaswami
|
The easiest way to think about variables in Dylan is to think
of them as labels you paste onto objects.
Imagine that you have an instance of <car>, maybe a VW
Beetle. When you do
let car-1 = make(<car>);
what happens is that first, you create a <car>, and then you
use the let operation to paste a little sticky note labeled "car-
1" onto it.
Let's suppose that car.odometer == 0, since this is a new
<car>. So if you then do:
let car-2 = car-1;
what this means is "find the object with the sticky-note 'car-
1', and paste another note labelled 'car-2' to it." You haven't
copied anything -- you now have a <car> with two sticky
labels on it. So if you then
drive-ten-thousand-miles(car-1);
Naturally the odometer(car-1) and odometer(car-2) will have
the same reading, 10000, since they both refer to the same
<car>, and it just drove ten thousand miles.
|
|
|
|
|
|
|
Peter
Hinely
|
Some aspects of Dylan's syntax bother me
(maybe because I don't know what I am doing.
:)
let x = 1; // declares a local
variable
// and binds
it to an object
x := 2;
// binds local variable to
// another
object
My question is, why do the two statements use a different
operator?
The assignment operator := vs. equality operator =
Why didn't the designers of Dylan use the syntax:
let x := 1;
It would seem to be more consistent syntax if they used the
same operator. Both statements are assignments, even though
the first statement initializes x. The only other difference that I
can think of is that the scope of x begins right after the end of
the "let" declaration statement that declares x.
let x = x + 1;
// ERROR
x := x + 1;
// VALID
Anyone want to comment on := vs. = ?
|
|
Scott Fahlman
|
It was felt by the majority of the people involved in the decision
that creation and initial binding of a variable is a conceptually
different beast from reassignment of a variable. The latter is a
non-functional, destructive operation and perhaps needs to be
used with a bit more care. So it was felt that these operations
shold look different.
I can see their point, but I think I would probably have gone the
other way on this, using := for both. For me it's close to a toss-
up, however, and some of the people who prefer that the two
forms look different feel pretty strongly about it.
|
|
Antoun
Kanawati
|
> let x = 1; // declares
a local variable
>
// and binds it to an object
>
> x := 2; // binds local variable to
> // another object
The first form introduces 'x' into the local environment, and
associates it with an initial value. The second form changes the
association of the symbol x with an object, but does not
introduce any new bindings.
Dylan comes from a LISP heritage, where variables REFER to
values, not contain values (like in C, or Pascal).
> My question is, why do the two statements use
a
> different operator?
> The assignment operator :=
vs. equality operator =
Actually, you're comparing the "let" binding form to assignment,
which is not correct. The "=" operator in "let" is pure syntactic
sugar, whereas the ":=" operator is the defining characteristic of
an assignment expression.
> Why didn't the designers of Dylan use the syntax:
> let x := 1;
> It would seem to be more consistent syntax if
they
> used the same operator. Both statements are
> assignments, even though the first statement initializes x.
The two expressions are not assignment. The first is a binding
form, whereas the second is a destructive assignment. There
are significant differences between the two. The first's effect
(introducing x into the lexical scope) is a compile time concept,
whereas the second is a runtime mutation.
> The only other difference that I can think of
> is that the scope of x begins right after the end
> of the "let" declaration statement that declares x.
Actually, there is a significant difference in meaning. I'll use the
Lispish syntax to demonstrate:
(let ((x 3)) (+ x 1))
means
((lambda (x) (+ x 1)) 3)
That is, it is not assignment, whereas the second form
explicitely means assignment.
|
|
Nick Kramer
|
C users probably don't see the distinction. C++ users, though,
shouldn't be entirely unfamiliar with the idea. C++ draws a
distinction between initialization and assignment. I've been
bitten at least once by this:
class FOO {
FOO();
FOO(FOO& original); // Called a copy
// constructor
FOO& operator= (FOO& foo1, FOO& foo2) {
.... };
}
void main (void)
{
FOO foo1;
foo1.something();
FOO foo2 = foo1; // Invokes the copy
// constructor,
// *not* the = operator!
}
Anyhow, my point is merely that Dylan's distinction between
binding and assignment is not entirely unheard of, even by C++
programemrs.
|
|
|
|
|
|
|
Peter
Hinely
|
Why is the syntax for the declaration of global and local
variables so different? It seems inconsistent.
// Declares and initializes
define variable x = 1; // a global variable
let x = 1; // a local
variable
Why didn't they use a syntax like:
define global variable x = 1;
define local variable x = 1;
(or some shortened version of something like that.)
The same can be said of methods:
define method my-method (x)
x;
end method
local method my-method (x)
x;
end;
The words to initialize the global vs. local functions do not
follow a logical syntax.
Also the statments that end the methods are not the same.
"end method" vs. "end;"
Also local methods are intialized with the syntax "local
method", but local variables are initialized with "let". It seems
very inconsistent to me.
Or is there something more I should consider? (I know realize
that "global" methods provide for generic functions.)
|
|
Scott E.
Fahlman
|
> // Declares and initializes
> define variable x = 1; // a global variable
> let x = 1; // a local
variable
Well, maybe this is an inheritance from Common Lisp.
"Define" is a top-level, global sort of thing.
But creating a global variable is a more heavyweight operation,
with more consequences.
|
|
Paul Haahr
|
>Also the statments that end the methods are not the
same.
> "end method" vs. "end;"
The word ``method'' is optional but permitted after ``end'' in both
cases. I don't see what the problem is.
|
|
Scott E.
Fahlman
|
It is curious that in the DIRM examples the "method" is pretty
consitently omitted for local declarations and is kept for global
ones.
That can be misleading, but the published grammar seems to
be OK on this -- just hard to read.
|
|
Kim Barrett
|
The inconsistency being discussed here doesn't actually exist.
In the BNF from the DIRM, the syntax for both "local" and
"define method" eventually reach "method-definition" as a
common tail.
"method-definition" ends with "end method(opt) SYMBOL(opt)",
so there's no difference between "local" and "define method" in
this regard.
|
|
|
|
|
|
|