Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

A6: multi promotion

10 views
Skip to first unread message

Richard Proctor

unread,
Mar 11, 2003, 9:42:18 AM3/11/03
to perl6-l...@perl.org
If one has a simple sub such as factorial:

sub factorial(int $a) {...}

then one subsequently declares the multi form of factorial to pick up the
non-integer form:

multi factorial(num $a) {...}

Does this promote the original declaration of factorial to a multi?
if not what happens?

Richard

--
Personal Ric...@waveney.org http://www.waveney.org
Telecoms Ric...@WaveneyConsulting.com http://www.WaveneyConsulting.com
Web services Ric...@wavwebs.com http://www.wavwebs.com
Independent Telecomms Specialist, ATM expert, Web Analyst & Services

Michael Lazzaro

unread,
Mar 11, 2003, 2:06:20 PM3/11/03
to Richard Proctor, perl6-l...@perl.org
On Tuesday, March 11, 2003, at 06:42 AM, Richard Proctor wrote:
> If one has a simple sub such as factorial:
>
> sub factorial(int $a) {...}
>
> then one subsequently declares the multi form of factorial to pick up
> the
> non-integer form:
>
> multi factorial(num $a) {...}
>
> Does this promote the original declaration of factorial to a multi?
> if not what happens?

I would *strongly* suspect that it would fail, saying "can't redeclare
'factorial'" or something. The idea behind C<multi> is that if you're
giving multiple possible signatures to a function, you have to do so
*explicitly*. Otherwise, you might just be accidentally overriding
some previous sub when you didn't really mean to -- and you'd _really_
like to know when that was happening.

sub foo($a,$b,$c) {...}

... lots of code inbetween ...

multi foo(int $a) {...}
multi foo(str $a) {...}

You might have forgotten, when declaring the two C<multi>s, that --
oops -- you had already used that subroutine name for something
completely different! So you'd want it to tell you if you were
redefining the C<foo> sub.

So I'm betting it's an error. You have to go back and make the first
one a C<multi>, if that's what you really meant to do.

(Note that this means you can't give alternate signatures to functions
that you've pulled in from a CPAN-style library, unless the library has
given you permission to do so by making the functions C<multi> in the
first place. Probably a good idea, on balance. You can do similar
things with wrapper functions.)

MikeL

Austin Hastings

unread,
Mar 11, 2003, 2:19:38 PM3/11/03
to Michael Lazzaro, Richard Proctor, perl6-l...@perl.org

But you can't wrap multi-ness, as far as I can tell.

[A6]
And it happens that the multimethod dispatch is smart enough to find
the ordinary single-invocant sysread method, even though it may not
have been explicitly declared a multimethod. Multimethod dispatch
happens to map directly onto ordinary method dispatch when there's only
one invocant.

At least, that's how it works this week...
[/A6]

To me, this suggests that multithods can be bolted on top of unithods.
And presumably, likewise multisubs can be bolted atop Plain Old Subs
(POSs).

So:

module CPANthing;
sub foo($x) {...}


module MyThing;
sub foo($x) {...} # ERROR - redefined: multi won't, but "my" will fix
sub foo(int $x) {...} # ERROR - must use multi

multi foo(int $x) {...} # OK
multi foo($x: @a) {...} # OK
multi foo($x: ?$y = 0) {...} # WARNING: compile-time or run-time?

=Austin

Michael Lazzaro

unread,
Mar 11, 2003, 2:58:43 PM3/11/03
to Austin_...@yahoo.com, Richard Proctor, perl6-l...@perl.org

On Tuesday, March 11, 2003, at 11:19 AM, Austin Hastings wrote:
> But you can't wrap multi-ness, as far as I can tell.
>
> [A6]
> And it happens that the multimethod dispatch is smart enough to find
> the ordinary single-invocant sysread method, even though it may not
> have been explicitly declared a multimethod. Multimethod dispatch
> happens to map directly onto ordinary method dispatch when there's only
> one invocant.
>
> At least, that's how it works this week...
> [/A6]
>
> To me, this suggests that multithods can be bolted on top of unithods.
> And presumably, likewise multisubs can be bolted atop Plain Old Subs
> (POSs).

I *think* that's just talking about the internal dispatch mechanism...
e.g. the dispatcher treats one-invocant subs and multis the same way,
and can map between them if necessary -- I don't think it's talking
about the semantic/syntactic rules for declaring them, just the guts of
what happens when you eventually call one.

So I still surmise it's a semantic error to override a C<sub> with a
C<multi>. (It doesn't have to be, internally, but otherwise I'm not
sure why you'd want a separate keyword C<multi> at all.)

>> sub foo($a,$b,$c) {...}
>>
>> ... lots of code inbetween ...
>>

>> multi foo(int $a) {...} # ERROR - can't multi an existing sub
>> multi foo(str $a) {...}

In your example, where you have two separate modules that both have a
C<foo>:

> module CPANthing;
> sub foo($x) {...}
>
> module MyThing;
> sub foo($x) {...} # ERROR - redefined: multi won't, but "my" will fix
> sub foo(int $x) {...} # ERROR - must use multi
>
> multi foo(int $x) {...} # OK
> multi foo($x: @a) {...} # OK
> multi foo($x: ?$y = 0) {...} # WARNING: compile-time or run-time?

I would put the errors in different places, because there's nothing
wrong with having an identically named function in two different
namespaces, right? So I would rewrite that:

> module CPANthing;
> sub foo($x) {...}
>
> module MyThing;

> sub foo($x) {...} # OK - we're in a different namespace now


> sub foo(int $x) {...} # ERROR - must use multi
>

> multi foo(int $x) {...} # ERROR - can't multi an existing sub
> multi foo($x: @a) {...}


> multi foo($x: ?$y = 0) {...}

Or am I completely spacing on something?

MikeL

Austin Hastings

unread,
Mar 11, 2003, 3:39:11 PM3/11/03
to Michael Lazzaro, perl6-l...@perl.org

--- Michael Lazzaro <mlaz...@cognitivity.com> wrote:
>

You want C<multi> to tell the compiler to build in multiple dispatch.
Any invocation of C<foo> after C<multi foo> is going to be a penny
dropped into the great Pachinko game of multimethod-dispatchery. By
default, if no winning multi appears, the call falls out the bottom and
winds up invoking the original sub().

> >> sub foo($a,$b,$c) {...}
> >>
> >> ... lots of code inbetween ...
> >>
> >> multi foo(int $a) {...} # ERROR - can't multi an existing
> sub
> >> multi foo(str $a) {...}
>
> In your example, where you have two separate modules that both have a
>
> C<foo>:
>
> > module CPANthing;
> > sub foo($x) {...}
> >
> > module MyThing;
> > sub foo($x) {...} # ERROR - redefined: multi won't, but "my" will
> fix
> > sub foo(int $x) {...} # ERROR - must use multi
> >
> > multi foo(int $x) {...} # OK
> > multi foo($x: @a) {...} # OK
> > multi foo($x: ?$y = 0) {...} # WARNING: compile-time or run-time?
>
> I would put the errors in different places, because there's nothing
> wrong with having an identically named function in two different
> namespaces, right? So I would rewrite that:
>

That's my bad. I should have made it more clear that I was moving the
CPAN stuff into the same namespace, either via use CPANthing qw(foo);
or by making them both *foo() for global.

To wit:

module CplusplusIO;
sub *infix:<<(IOstream $io, Scalar $s) {...}

module MyThing;
sub *infix:<<(IOstream $io, Cat $c) {...} # I think this doesn't work

multi *infix:<<(IOstream $io, Cat $c) {...} # Should work

multi *infix:<<(IOstream $io, wchar_t $c) {...} # Woo-hoo! Next: STL

My point is that if some CPAN author writes a cool module, and you use
it and give over a piece of your namespace by importing the symbols,
you should still be able to extend the CPAN module to the specific bits
of your own data by bolting on multimethod that will make the
invocation of the actual sub an acceptable thing.

Further, since you don't necessarily KNOW going in that you're going to
rework some or all of the interface, you don't want to try wrapping
everything up:

module CPANthing;
sub foo(str $x) {...};

module MyThing;
use CPANthing qw(foo(str $));

# Are siglets or psiglets going to be used for importing possibly
# ambiguous symbols now?

# ...
my $a = "Hello, world!";

foo($a);


# Okay so far, right?

# But now you have some complex widget:

my BigThingy $bt = init_big_thingy($a);

foo($bt);

# Except that $bt.tostr() yields debug info. So I really want a special

# function that will do this for me:

# Must be multi since I imported C<foo>
multi foo(BigThingy $bt) { foo($bt.description); }

# Now they both work:

foo($a); # Calls sub CPANthing::foo() via local namespace due to import
foo($bt); # Calls multi MyThing::foo() locally, which passes on ...

=Austin


Damian Conway

unread,
Mar 11, 2003, 7:54:21 PM3/11/03
to Richard Proctor, perl6-l...@perl.org
Richard Proctor asked:

> If one has a simple sub such as factorial:
>
> sub factorial(int $a) {...}
>
> then one subsequently declares the multi form of factorial to pick up the
> non-integer form:
>
> multi factorial(num $a) {...}
>
> Does this promote the original declaration of factorial to a multi?
> if not what happens?

What happens is that the subroutine hides the multimethod
(thoroughout the subroutine's package).

We didn't include it in A6 but our current notions (i.e. this week ;-)
about interactions between subs, methods, and multimethods are
something like this:

Dispatched to first existing
Call and visible of...
==================== ============================

foo($x, $y); 1. C<sub DISPATCH>
2. C<multi DISPATCH>
3. C<sub foo>
4. C<sub AUTOLOAD>
5. C<multi foo>
6. C<multi AUTOLOAD>


Invocant.foo($x, $y) 1. C<method Invocant::DISPATCH> or inherited
2. C<multi DISPATCH>
3. C<method Invocant::foo> or inherited
4. C<method Invocant::AUTOLOAD> or inherited
5. C<multi foo>
6. C<multi AUTOLOAD>

In practice, the C<DISPATCH> sub/method would rarely ever be used.
It's just a hook by which to grab control of the dispatch process itself.

In the case of subroutines, there are three subcases for each
subroutine dispatch. That is, if the table above says:

3. C<sub whatever>

that's really:

3a. Any in-scope C<my sub whatever>
3b. The current package's C<our sub whatever>
3c. The global C<sub *whatever>


Damian

Michael Lazzaro

unread,
Mar 11, 2003, 9:08:59 PM3/11/03
to Austin_...@yahoo.com, perl6-l...@perl.org

On Tuesday, March 11, 2003, at 12:39 PM, Austin Hastings wrote:
> You want C<multi> to tell the compiler to build in multiple dispatch.
> Any invocation of C<foo> after C<multi foo> is going to be a penny
> dropped into the great Pachinko game of multimethod-dispatchery. By
> default, if no winning multi appears, the call falls out the bottom and
> winds up invoking the original sub().

OK, hmm. What Damian is saying is that, tentatively, it's the
reverse... it calls the sub if theres a sub, then the multi if there's
a multi. So overriding a sub with a multi wouldn't work, but it would
*silently* not work, because you could just never get to the multi
version (well, not without a bit of introspection).

I agree that the issue of overriding an inherited/preexisting C<sub> --
like one from a CPAN module -- with a set of C<multi> implementations
is a useful capability; it would allow you to extend predefined
routines to handle different arguments without getting into OO. But I
sure worry that it makes accidental redefinition of subs invisible in
many cases.

Dunno. I could argue that one both ways. Maybe it has to wait until
A12.


>>>> sub foo($a) {...}


>>>>
>>>> ... lots of code inbetween ...
>>>>

>>>> multi foo(int $a) {...} # (???)
>>>> multi foo(str $a) {...}

MikeL

Richard Proctor

unread,
Mar 12, 2003, 1:57:12 AM3/12/03
to Michael Lazzaro, perl6-l...@perl.org, Austin_...@yahoo.com
On Wed 12 Mar, Michael Lazzaro wrote:
>
> On Tuesday, March 11, 2003, at 12:39 PM, Austin Hastings wrote:
> > You want C<multi> to tell the compiler to build in multiple dispatch.
> > Any invocation of C<foo> after C<multi foo> is going to be a penny
> > dropped into the great Pachinko game of multimethod-dispatchery. By
> > default, if no winning multi appears, the call falls out the bottom and
> > winds up invoking the original sub().
>
> OK, hmm. What Damian is saying is that, tentatively, it's the
> reverse... it calls the sub if theres a sub, then the multi if there's
> a multi. So overriding a sub with a multi wouldn't work, but it would
> *silently* not work, because you could just never get to the multi
> version (well, not without a bit of introspection).

Messy

>
> I agree that the issue of overriding an inherited/preexisting C<sub> --
> like one from a CPAN module -- with a set of C<multi> implementations
> is a useful capability; it would allow you to extend predefined
> routines to handle different arguments without getting into OO. But I
> sure worry that it makes accidental redefinition of subs invisible in
> many cases.
>

Perhaps it should be possible to explicitly promote (or is it demote?):

sub factorial is multi;

Larry Wall

unread,
Mar 12, 2003, 1:15:55 PM3/12/03
to perl6-l...@perl.org
On Tue, Mar 11, 2003 at 06:08:59PM -0800, Michael Lazzaro wrote:
:
: On Tuesday, March 11, 2003, at 12:39 PM, Austin Hastings wrote:
: >You want C<multi> to tell the compiler to build in multiple dispatch.
: >Any invocation of C<foo> after C<multi foo> is going to be a penny
: >dropped into the great Pachinko game of multimethod-dispatchery. By
: >default, if no winning multi appears, the call falls out the bottom and
: >winds up invoking the original sub().

No, a sub declaration must continue to be a strong claim that it is
the only implementation *in scope*.

: OK, hmm. What Damian is saying is that, tentatively, it's the

: reverse... it calls the sub if theres a sub, then the multi if there's
: a multi. So overriding a sub with a multi wouldn't work, but it would
: *silently* not work, because you could just never get to the multi
: version (well, not without a bit of introspection).

It would silently not work in exactly the same sense that any other sub
that is out of scope is invisible.

: I agree that the issue of overriding an inherited/preexisting C<sub> --

: like one from a CPAN module -- with a set of C<multi> implementations
: is a useful capability; it would allow you to extend predefined
: routines to handle different arguments without getting into OO. But I
: sure worry that it makes accidental redefinition of subs invisible in
: many cases.

There's no reason you can't write a multi that calls the sub. That multi
would be visible anywhere the sub wasn't.

Larry

Piers Cawley

unread,
Mar 12, 2003, 7:02:22 PM3/12/03
to Damian Conway, Richard Proctor, perl6-l...@perl.org
Damian Conway <dam...@conway.org> writes:

This looks like a classic Chain of Responsibility pattern. I wonder,
would it be possible to expose the dispatch system as an object
(chain) which could be modified by the courageous programmer so that
one could, for instance, set up a different dispatcher for the rest of
a lexical scope which dispatched to multimethods before subs/methods?

I know you could override it by implementing your own DISPATCH sub,
but an OO interface does have a certain appeal to it. Hell, one could
probably implement a suitable DISPATCH sub that would look for a
$*DISPATCHER and dispatch via that if it existed or just do a NEXT
call if it didn't. Something like this might do the trick:

sub DISPATCH {
if (defined $*DISPATCH) {
$*DISPATCHER.dispatch(@_);
}
else {
NEXT(@_);
}
}

So, even if there isn't a 'core' way of manipulating the current
dispatcher it's possible to write a module that would make it look
like you could.

Sometimes I scare myself.

--
Piers

Nicholas Clark

unread,
Mar 15, 2003, 6:27:03 AM3/15/03
to Piers Cawley, Damian Conway, Richard Proctor, perl6-l...@perl.org
On Thu, Mar 13, 2003 at 12:02:22AM +0000, Piers Cawley wrote:
> Damian Conway <dam...@conway.org> writes:

> > We didn't include it in A6 but our current notions (i.e. this week ;-)
> > about interactions between subs, methods, and multimethods are
> > something like this:
> >
> > Dispatched to first existing
> > Call and visible of...
> > ==================== ============================
> >
> > foo($x, $y); 1. C<sub DISPATCH>
> > 2. C<multi DISPATCH>
> > 3. C<sub foo>
> > 4. C<sub AUTOLOAD>
> > 5. C<multi foo>
> > 6. C<multi AUTOLOAD>

[snip similar methods]

> This looks like a classic Chain of Responsibility pattern. I wonder,
> would it be possible to expose the dispatch system as an object
> (chain) which could be modified by the courageous programmer so that
> one could, for instance, set up a different dispatcher for the rest of
> a lexical scope which dispatched to multimethods before subs/methods?
>
> I know you could override it by implementing your own DISPATCH sub,

> So, even if there isn't a 'core' way of manipulating the current


> dispatcher it's possible to write a module that would make it look
> like you could.

I think that it would be nice to be able to chain yourself in there, rather
than having to replace.

In perl5 there are some things you have to override, rather than adding to.
Offhand I can't see a practical way that the opcode loop or the regexp engine
could be done in any other way, but IIRC require is hookable, and
UNIVERSAL::AUTOLOAD and the core ops have to be replaced, when what you might
like to do is just wrap to extend things. Or will wrapping these routines be
good enough?

Sort of related - if you wrap a subroutine, and then something else wraps
the subroutine (and hence wraps you), can you pull your wrapper off without
everything getting confused? (ie their wrapper now directly wraps the
original)

ie if Acme::Foo wraps &DISPATCH, then Acme::Bar is loaded at it wraps
&DISPATCH, then the script removes Acme::Foo, is the wrapping now as if
only Acme::Bar was the only module ever loaded?

Nicholas Clark

Larry Wall

unread,
Mar 15, 2003, 11:16:18 AM3/15/03
to perl6-l...@perl.org
On Sat, Mar 15, 2003 at 11:27:03AM +0000, Nicholas Clark wrote:
: I think that it would be nice to be able to chain yourself in there, rather

: than having to replace.
:
: In perl5 there are some things you have to override, rather than adding to.
: Offhand I can't see a practical way that the opcode loop or the regexp engine
: could be done in any other way, but IIRC require is hookable, and
: UNIVERSAL::AUTOLOAD and the core ops have to be replaced, when what you might
: like to do is just wrap to extend things. Or will wrapping these routines be
: good enough?

Maybe. For dispatch-related subs, it will depend heavily on how the
run-time system optimizes dispatches. No way are we going through
that entire list on every dispatch without some kind of caching of
the result so that we can dispatch more directly next time. (Perl 5
already does this with regular method dispatches.) But if we depend
on wrapping, and if wrapping becomes a common occurrence at run time,
and if we have to recalcuate our dispatches every time someone wraps
a routine, then we're gonna be hosed on the performance front.

: Sort of related - if you wrap a subroutine, and then something else wraps


: the subroutine (and hence wraps you), can you pull your wrapper off without
: everything getting confused? (ie their wrapper now directly wraps the
: original)

Yes, that's why you .unwrap using the id that .wrap returned.

: ie if Acme::Foo wraps &DISPATCH, then Acme::Bar is loaded at it wraps


: &DISPATCH, then the script removes Acme::Foo, is the wrapping now as if
: only Acme::Bar was the only module ever loaded?

Well, the way you've said it makes it sound like the wrapper is
going to be pointing at a module Acme::Foo that isn't there anymore.
I assume by "removes Acme::Foo" you really mean "removes the wrapper
installed by Acme::Foo". Certainly if you call the .wrap and .unwrap
methods properly, everything should stay in sync. We've not generally
been in the habit of catering to people who remove modules, but if we
did want to support that, we'd probably have some kind of destructor
for the module that could unwrap things wrapped by the module.

Larry

Dan Sugalski

unread,
Mar 15, 2003, 2:08:11 PM3/15/03
to Larry Wall, perl6-l...@perl.org
At 8:16 AM -0800 3/15/03, Larry Wall wrote:
>On Sat, Mar 15, 2003 at 11:27:03AM +0000, Nicholas Clark wrote:
>: I think that it would be nice to be able to chain yourself in there, rather
>: than having to replace.
>:
>: In perl5 there are some things you have to override, rather than adding to.
>: Offhand I can't see a practical way that the opcode loop or the
>regexp engine
>: could be done in any other way, but IIRC require is hookable, and
>: UNIVERSAL::AUTOLOAD and the core ops have to be replaced, when
>what you might
>: like to do is just wrap to extend things. Or will wrapping these routines be
>: good enough?
>
>Maybe. For dispatch-related subs, it will depend heavily on how the
>run-time system optimizes dispatches. No way are we going through
>that entire list on every dispatch without some kind of caching of
>the result so that we can dispatch more directly next time. (Perl 5
>already does this with regular method dispatches.) But if we depend
>on wrapping, and if wrapping becomes a common occurrence at run time,
>and if we have to recalcuate our dispatches every time someone wraps
>a routine, then we're gonna be hosed on the performance front.

I hadn't actually planned on doing anything special--when you wrap a
sub, the PMC that represents the sub gets its code pointer replaced
with the wrapping sub PMC, which itself holds the wrapped code
pointer. Wrap something and generally nobody knows the difference,
because the outside interface and address doesn't change.

Unwrapping puts the pointer back again and removes the under-the-hood
messing about, once again transparently.
--
Dan

--------------------------------------"it's like this"-------------------
Dan Sugalski even samurai
d...@sidhe.org have teddy bears and even
teddy bears get drunk

0 new messages