develooper Front page | perl.perl6.language | Postings from September 2005

Re: multi scoping

Thread Previous
From:
Luke Palmer
Date:
September 4, 2005 02:04
Subject:
Re: multi scoping
Message ID:
7ca3f0160509040204ca996ee@mail.gmail.com
On 9/4/05, Yuval Kogman <nothingmuch@woobling.org> wrote:
> I always saw scoping of multis as something that applies to the
> variants...
> 
>                 multi sub foo {
> 
>                 }
> 
>                 {
>                         my multi sub foo {
> 
>                         }
> 
>                         # the second variant is just for this scope, but neither masks
>                         # the other
>                 }

Reading over A12 again (in the section "Multiple Dispatch"), it
appears that you are right.

So let's figure out if that's actually the right way or the wrong way.
 I don't understand the second-to-last paragraph of that section,
which apparently explains why single variants mask.

> > You must explicitly ask for masking
> 
> I think this should be an option... You can either mask off a single
> variant by declaring one that overrides it in a tighter scope, with
> yadda yadda as the body, or you could ask a whole scope to be
> omitted from the possible variants.

Just to clarify, is this what you are suggesting?

    multi foo (Int $x) { $x + 1 }
    multi bar (Int $x) { $x + 1 } 
    {
        my sub foo ($x) {...}
        my multi foo (Str $x) { "x" ~ $x }
        
        my multi bar (Str $x) { "x" ~ $x }

        bar("hi"); # xhi
        bar(1);    # 2
        foo("hi"); # xhi
        foo(1);    # error, no compatible sub found
    }

Though if that's the case, then it almost feels to me like the "multi"
should go before the "my".  But that would be screwing with the
consistency of the grammar a little too much, I think.

> >     2) Make multi automatically find the symbol that you would have
> > referred to if the definition had not been there, and add a multi case
> > to that symbol.  So in the example above, the innermost infix:<+> that
> > existed before you said "multi" was *infix:<+>, so the multi
> > definition would basically infer that you meant to say multi
> > *infix:<+> and do the right thing.
> 
> I don't agree with this... It takes the lexical scoping semantics
> out of things.

Oh, by the way, I was suggesting that a *bare* "multi" would do this. 
If you said "our" or "my", you are stating exactly what you mean. 
Well, almost (what does "my multi foo" mean if there is an outer
lexical multi foo -- mask or append?).

> There is one more problem though:
> 
>         class Complex {
>                 multi sub &infix:<*> { ... }
>         }
> 
>         package Moose;
>         use Complex;
>         use SomeMathLib <function>;
> 
>         ...
> 
>         function($some_complex_number); # if function calls infix:<*> on
>         # it's operand, somehow... What happens?

This is actually my *whole* problem, and the reason that I don't think
that variants should mask, but entire symbols should mask.  It seems
like you'd use a lexically scoped multi variant about as much as you'd
use a lexically scoped method on a class.  Can you think of a use for
that?

Isn't the point of lexical scoping so that you don't have to worry
whether somebody else called something the same thing you did?  I can
picture this:

    multi combine (Any $x, Any $y) { ZCombinator.new($x, $y) }
    multi combine (@x, @y)         { ZList.new([ @x, @y ]) }   # concat
    multi combine (Str $x, Str $y) { ZStr.new($x ~ $y) }

    # ... many lines pass ...

    sub process($x, $y) {
        my multi combine (Any $a, Any $b) { die "Cannot combine" }
        my multi combine (Int $a, Int $b) { $a + $b }
        
        return combine($x, $y);
    }

    process("Foo", "Bar");
    # Gets back a... what? ZStr?  What the heck is that

Clearly the author of process intended that two integers get added and
anything else dies.  He was not aware of the combine defined many many
lines above.   But he was smart and made his multis lexically scoped
so he didn't have to worry.

Really, he should have written process like so:

    sub process($x, $y) { 
        my sub combine ($a, $b) {...}
        my multi combine (Any $a, Any $b) { die "Cannot combine" }
        my multi combine (Int $a, Int $b) { $a + $b }
        
        return combine($x, $y);
    }

That case, to me, seems a lot more common than lexically overriding a
multi variant.  But I am open to counterexamples.

Assuming we have no information about the frequency, there is another
question to ask whether it makes sense to override or extend:  Is it
natural to do it both ways?  That is, does it feel right?  (Please,
get your mind out of the gutter and pay attention! :-)

It seems fairly natural when we go with the extension mechanism that
A12 seems to propose.  "multis extend, subs mask", so if you want to
mask, you just define it as a sub first.  It's a little awkward, but
it'll do.  The other way, however, is not nearly as natural:
    
    # let's say that he wanted process() to do what I said he didn't intend
    sub process($x, $y) { 
        my multi combine (Any $x, Any $y) { OUTER::combine($x, $y) }
        my multi combine (Int $x, Int $y) { $x + $y }
        ...
    }

And if you pretend that the Strs above were actually Ints, and that
these Ints were actually Nums, then the semantics wouldn't work out
right still.  That kind of screams for a trait:

    sub process($x, $y) {
        my multi combine ($x, $y) is extended {...}
        my multi combine ...
        ...
    }

And then it's pretty much the inverse of what we had before, plus an
"is extended" trait.  And so I don't think the naturalness argument
gets us anywhere, except for the fact that we wouldn't have to define
a trait.

I guess I'm really arguing for masking by default instead of extention
on the grounds of the principle of least surprise.  I also like to
think of names as concepts, such that if you're extending the concept,
you ought to rename it.  In that respect, lexically overriding multi
variants instead of whole symbols at a time feels like lexically
scoped classes automatically being derived from outer classes of the
same name.  And we all know that that is Just Wrong.

Luke

Thread Previous


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About