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