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