Expected Common Misunderstanding (was: bug: run/3 bug)
Mark S. Miller
markm@caplet.com
Mon, 07 Jun 1999 08:01:11 -0700
Since the following email raises an issue of general E interest, I'm taking
the liberty of cc'ing the e-lang list. As this is the third email for which
I'm taking such liberties, I should probably state a policy. Here goes:
Feel free to send me private email about E, but unless you ask that
responses *not* be cc'ed to the e-lang list, I will feel free to do so if I
feel it appropriate, and if it isn't obviously private.
>At 10:29 AM 6/6/99 , Marc Stiegler wrote:
>Got it in elmer. Another good updoc test.
>
>--marcs
> ? def build3(a,b,c) {
> > 3
> > }
> # value: <build3>
>
> ? def build3(a,b) {
> > build3(a,b,3)
> > }
> # value: <build3>
>
> ? build3(1,2)
> # problem: <NoSuchMethodException: run/3>
This is not an error, E is doing what it's supposed to do. However, the
reasons you thought it was an error will be shared by many others. This
example shows an important difference between functions and methods. One
could accuse E of leading people into this soon-to-be-common confusion by
making functions and methods seem so much alike.
To see why E isn't in error, let's expand your example:
def build3 {
to run(a,b,c) {
3
}
}
def build3 {
to run(a,b) {
build3 run(a,b,3)
}
}
These define two objects. If these are both defined in a non-interactive
context, ie, within a *.e or *.emaker file, then the second definition of
the same name does indeed give a
# problem: <AlreadyDefinedException: conflicting definition of build3>
as expected. In an interactive context, such as elmer or the interactive E
command line, the second definition overrides the first. The first object
is an object with only a 3-arity "run" method, ie, a 3-arity function. The
second object has only a 2-arity "run" method. The 3-argument call to
build3 is calling the build3 object which is in scope. This is the second
one. The fact that this object doesn't have a 3-arity run method doesn't
change the fact that it is the one in scope.
The program you "wanted to" write is:
? def build3 {
> to (a,b,c) {
> 3
> }
> to (a,b) {
> build3(a,b,3)
> }
> }
# value: <build3>
? build3(1,2)
# value: 3
Which expands to:
? def build3 {
> to run(a,b,c) {
> 3
> }
> to run(a,b) {
> build3 run(a,b,3)
> }
> }
# value: <build3>
? build3 run(1,2)
# value: 3
This build3 is both a 2-arity function and a 3-arity function, in that it
has both arities of "run" method. Though it has both behaviors, there's
only one object, and both calls to build3 are invoking that same object.
Unless someone has a super-clever idea, I'd guess we are simply stuck with
people often falling into this trap, and all we can do is explain the issue
well. Besides an explanation, it often helps to have a slogan:
Functions are objects. Methods aren't. Methods simply describe what
objects do.
Overloaded,
--MarkM