Virtual methods

3 views
Skip to first unread message

Aaron Sherman

unread,
May 18, 2005, 10:09:25 AM5/18/05
to Perl6 Language List
In Perl 6, I don't think we need to tag methods as "virtual" like C++
does, since we have the handy yadda, yadda to do that for us.

However, there is a variant of C++'s virtual that I'd love to see. By
default a role cannot override the methods of a class, but if it could
override those methods specifically marked with the virtual trait, then
we could define stub methods in classes that don't have a specific
behavior until a more concrete role is mixed in.

This gives you a form of auto-loading like delegation, but with less
storage overhead (since there's no encapsulation until you need it).
Here's an example:

role X {
has Str $.string handles<ucfirst>;
# I'll write something like an accessor to avoid brining
# up some questions around how virtual methods interact
# with auto-accessors just yet.
method setstring(Str $string) { $.string = $string }
}
class Y {
method setstring(Y $me: Str $string)
is virtual {
$me does X;
$me.setstring($string);
}
}
my Y $var;
$var.setstring("hello, world"); # overrides setstring
say $var.ucfirst; # says "Hello, world"
$var.setstring("bye, now"); # calls existing setstring
say $var.ucfirst; # says "Bye, now"

You can probably tell that I'm about to suggest that this would be the
most efficient way to implement the dynamic functionality of Any, and
given the recent ponie/parrot discussions around flags, I think using
virtual methods as flags is probably the right way to go....

--
Aaron Sherman <a...@ajs.com>
Senior Systems Engineer and Toolsmith
"It's the sound of a satellite saying, 'get me down!'" -Shriekback


Luke Palmer

unread,
May 18, 2005, 10:51:22 AM5/18/05
to Aaron Sherman, Perl6 Language List
On 5/18/05, Aaron Sherman <a...@ajs.com> wrote:
> In Perl 6, I don't think we need to tag methods as "virtual" like C++
> does, since we have the handy yadda, yadda to do that for us.
>
> However, there is a variant of C++'s virtual that I'd love to see. By
> default a role cannot override the methods of a class, but if it could
> override those methods specifically marked with the virtual trait, then
> we could define stub methods in classes that don't have a specific
> behavior until a more concrete role is mixed in.
>
> This gives you a form of auto-loading like delegation, but with less
> storage overhead (since there's no encapsulation until you need it).
> Here's an example:
>
> role X {
> has Str $.string handles<ucfirst>;
> # I'll write something like an accessor to avoid brining
> # up some questions around how virtual methods interact
> # with auto-accessors just yet.
> method setstring(Str $string) { $.string = $string }
> }
> class Y {
> method setstring(Y $me: Str $string)
> is virtual {
> $me does X;

Except that mixins like this always treat things as "virtual".
Whenever you mixin a role at runtime, Perl creates an empty, anonymous
subclass of the current class and mixes the role in that class. Since
roles beat superclasses, you'll always override your own methods.

Luke

Aaron Sherman

unread,
May 18, 2005, 11:13:08 AM5/18/05
to Luke Palmer, Perl6 Language List
On Wed, 2005-05-18 at 10:51, Luke Palmer wrote:

> Except that mixins like this always treat things as "virtual".
> Whenever you mixin a role at runtime, Perl creates an empty, anonymous
> subclass of the current class and mixes the role in that class. Since
> roles beat superclasses, you'll always override your own methods.

Ok, good point (I've even pointed that out to others before, and I
missed it)...

I know that's the way it works, but now it's really bothering me.

There are many gotchas that fall out of that. For example, you might
have a special role that overrides .print to handle structured data, so
your code says:

my Foo $obj;
given $obj {
when StructuredPrintRole {
# Someone's already taken care of it, possibly
# adding a more specialized role, so leave it
# alone.
}
default {
# Add in a boring default handler
$obj does StructuredPrintRole
}
}
$obj.print($structured_data);

Woefully, you lose is Foo happens to be DECLARED with
StructuredPrintRole, and it overrode print. But, if it just inherited a
print(), then it works. In other words, this code will mysteriously fail
the second someone innocently adds a print method to Foo!

Action at a distance... my head hurts.

Piers Cawley

unread,
May 25, 2005, 9:11:38 AM5/25/05
to Aaron Sherman, Luke Palmer, Perl6 Language List
Aaron Sherman <a...@ajs.com> writes:

Aaron, you do realise that that's quite obscene code don't you? I mean, you're
doing a case statement based on the type of its topic, and to compound the
evils, you're changing how your topic's print method works *everywhere* simply
to get your 'special' print behaviour. If you must do something like this (and
call it print), then write it as a multimethod or something.

Aaron Sherman

unread,
May 26, 2005, 10:46:45 AM5/26/05
to Piers Cawley, Perl6 Language List
On Wed, 2005-05-25 at 09:11, Piers Cawley wrote:
> Aaron Sherman <a...@ajs.com> writes:

> > There are many gotchas that fall out of that. For example, you might
> > have a special role that overrides .print to handle structured data, so
> > your code says:
> >
> > my Foo $obj;
> > given $obj {
> > when StructuredPrintRole {
> > # Someone's already taken care of it, possibly
> > # adding a more specialized role, so leave it
> > # alone.
> > }
> > default {
> > # Add in a boring default handler
> > $obj does StructuredPrintRole
> > }
> > }
> > $obj.print($structured_data);
> >

> > Woefully, you lose [if] Foo happens to be DECLARED with


> > StructuredPrintRole, and it overrode print. But, if it just inherited a
> > print(), then it works. In other words, this code will mysteriously fail
> > the second someone innocently adds a print method to Foo!
> >
> > Action at a distance... my head hurts.
>
> Aaron, you do realise that that's quite obscene code don't you?

Why yes, I think that was my point. This is an unfortunate consequence
of the proposed way of mixing in functionality. If this is not what
mixins are intended for, then I guess I'm way out in the tall grass, as
I'm nearly salivating over the potential.

> I mean, you're
> doing a case statement based on the type of its topic,

And there's something wrong with switching on type? ... I'm sure I have
no idea what's wrong with that. As long as smart matching does the right
thing with inheritance (and S04 indicates that .does is invoked), this
is a pretty traditional way to ask a variable if it "does" what you
want. You could explicitly call the .can method, but as this example
shows, you don't always want to know if the method EXISTS, but if it
supports functionality that you need.

> and to compound the
> evils, you're changing how your topic's print method works *everywhere* simply
> to get your 'special' print behaviour.

Yes, that is exactly the point. You have something that behaves like a
filehandle, and you wish to tell it to handle structured data. You do
this by first asking if it supports the appropriate interface (see S12
for how a role becomes an interface), and failing that, adding a default
implementation.

This probably looks something like:

role StructuredPrintRole {
has StructuredLayout $.sdlayout;
method print($me: *@list) returns Int {
$me.*SUPER::print(map:{ # A12 shorthand for WALK[:super]::
$_ ~~ StructuredData ??
$me.structure($me.sdlayout) ::
$me;
}, @list);
}
}

So, by default you can .print to our $obj, and if you support some sort
of structured data representation, the right thing happens (even though
the "normal" stringification of such a value would not be the same).

You might use this for, just as an example, protocol representation of
an XImage datastructure for the X protocol. The stringification of it
probably isn't the image data -- that wouldn't be terribly useful in
general -- but for writing to an X socket, you certainly do want the
data to be represented correctly, so you'd derive a role from
StructuredPrintRole and tell it how to format X protocol data correctly.

> If you must do something like this (and
> call it print), then write it as a multimethod or something.

I think you're thinking in terms of REPLACING functionality. I'm
thinking in terms of AUGMENTING functionality.

Reply all
Reply to author
Forward
0 new messages