Expected Common Misunderstanding (was: bug: run/3 bug)

Ben Laurie ben@algroup.co.uk
Mon, 07 Jun 1999 16:55:24 +0100


Mark S. Miller wrote:
> 
> 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.

You could simply generate a warning when in interactive mode, i.e.:

build3(a,b,c) has splatted existing build3(a,b).

Or is that too simpleminded?

Cheers,

Ben.

--
http://www.apache-ssl.org/ben.html

"My grandfather once told me that there are two kinds of people: those
who work and those who take the credit. He told me to try to be in the
first group; there was less competition there."
     - Indira Gandhi