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

A modest question

7 views
Skip to first unread message

Austin Hastings

unread,
Jan 7, 2004, 1:26:18 AM1/7/04
to Perl6 Language

Larry, chromatic, Allison, Damian, et al:

What's the big fascination with traits?

I read the paper, and at the end where they talked about refactoring the
Smalltalk class library, they managed to claim 12% fewer lines of code:

> In total, these classes use 46 different traits and implement 509 methods,
> whereof 36 are automatically generated accessor methods. This is just over
> 5% fewer methods than in the original implementation. In addition, the
code
> for the trait implementation is 12% smaller than the original.

However, the authors continue to report:

> This is especially remarkable because 10% of the methods in the original
> implementation are implemented “too high” in the hierarchy specifically to
> enable code sharing. With inheritance, the penalty for this is the
repeated
> need to cancel inherited behaviour (using methods that cause a runtime
> error) in subclasses where they do not make sense. In the trait
> implementation, there is no need to resort to this tactic.

Which I interpret as meaning that the "artificially high" code-sharing
implementations have caused extra lines of code in lower level classes. So
the "12%" figure is inclusive of deliberate pessimization that has been done
to substitute clarity for compactness -- presumably "native" code that
hasn't been evolved so much would have more compactness, less redundant
code, and would yield less than 12% improvement by refactoring into the
traits model.

So on the grand balance of utility, what are the metrics that traits are
supposed to help improve? The 10% reduction of LOC reminds me of the old
Assembler rule of thumb: do it last, you'll get 10% faster.

Presumably there are some other metrics being improved. Can one or more of
you explain what the perceived improvements, and what's the order of
significance? That is, give us a 1. 2. 3. list?

Thanks,

=Austin

Chromatic

unread,
Jan 7, 2004, 1:43:55 AM1/7/04
to Austin_...@yahoo.com, Perl6 Language
On Tue, 2004-01-06 at 22:26, Austin Hastings wrote:

> So on the grand balance of utility, what are the metrics that traits are
> supposed to help improve?

Two big ones:

- naming collections of behavior that are too fine-grained to fit into
classes cleanly
- enabling finer-grained code reuse

Consider a method that needs to print an object. You might require a
String:

sub print_it ( String $thingie )
{
print $thingie;
}

Why does it have to be a String, though? What prevents it from working
with anything that can stringify, besides the overly restrictive
signature? What if you could say (the Perl 6 equivalent of):

sub print_it ( does Stringify $thingie )
{
print $thingie.stringify();
}

That's both more general and something more specific. By asking for
what you really want, you're not coding everyone else into a corner.

Take Mail::SimpleList and Mail::TempAddress, for example. Both have
classes that represent individual addresses or mailing lists. The
appropriate parent class is Mail::Action::Address, which has the very
basic data and properties that both subclasses share.

Both simple lists and temp addresses should contain expiration dates, so
both classes need some sort of behavior to implement that.

When you throw another class into the mix, say, Mail::OneWayList, where
there's no expiration (trust me, even though it's not on the CPAN yet),
there's a problem.

I'd like to share code between all three classes that represent aliases
and Mail::Action::Address is the appropriate place to do that. I don't
want to share *all* of the code, though, so I can't really put the
expiration code in Mail::Action::Address.

I *could* subclass Mail::Action::Address and make
Mail::Action::Address::Expires and change the parent class of the temp
address and the simple list classes, but that's kinda icky as it leads
to yet another level in the class hierarchy.

By turning expiration into a role, though, everything can extend
Mail::Action::Address and only those classes that really need expiration
can do it -- and they share the code.

Contrived example? Maybe. Maybe not. Consider further James
Fitzgibbon's Mail::Action::Role::Purge. James wanted to extend all
Mail::Action subclasses to allow purging of expired addresses or lists.
That's reasonable, but it's not something I wanted to add to
Mail::Action because it doesn't know anything about expiration.

So he made it a role and decorates expirable objects with the role and
can do what he wants there.

Again, the goals are specificity, genericity, and improved reuse.

-- c

Jonathan Lang

unread,
Jan 7, 2004, 3:43:56 AM1/7/04
to Piers Cawley, chromatic, Austin_...@yahoo.com, Perl6 Language
Piers Cawley wrote:
> > Why does it have to be a String, though? What prevents it from
> > working with anything that can stringify, besides the overly
> > restrictive signature? What if you could say (the Perl 6 equivalent
> > of):
> >
> > sub print_it ( does Stringify $thingie )
> > {
> > print $thingie.stringify();
> > }
> >
> > That's both more general and something more specific. By asking for
> > what you really want, you're not coding everyone else into a corner.
>
> In this particular case I prefer the Smalltalk thing of requiring all
> objects to have a C<print_on($aStream)> method and not even requiring
> that C<$aStream> satisfy the Stream role. But that's probably a matter
> of taste.

Maybe as an alternative to

role Stringify {must stringify();}
sub print_it (Stringify $thingie) {print $thingie.stringify();}

you might be able to say

sub print_it ($thingie must stringify()) {print $thingie.stringify();}

Hmm... there's a certain elegance to being able to specify one or two
requirements directly in a signature.

Also: in the first of these two, would classof($thingie) actually have to
have Stringify as a role, or would it be reasonable to instead say that
classof($thingie) must meet Stringify's demands? The latter would require
more work on the compiler's part, but would be considerably more flexible.
Perhaps "Stringify $thingie" requires that the Stringify role must
actually be used, while something like "$thingie like Stringify" would
only require that Stringify's demands be met?

=====
Jonathan "Dataweaver" Lang

__________________________________
Do you Yahoo!?
Yahoo! Hotjobs: Enter the "Signing Bonus" Sweepstakes
http://hotjobs.sweepstakes.yahoo.com/signingbonus

Piers Cawley

unread,
Jan 7, 2004, 3:28:12 AM1/7/04
to chromatic, Austin_...@yahoo.com, Perl6 Language
chromatic <chro...@wgz.org> writes:

> On Tue, 2004-01-06 at 22:26, Austin Hastings wrote:
>
>> So on the grand balance of utility, what are the metrics that traits are
>> supposed to help improve?
>
> Two big ones:
>
> - naming collections of behavior that are too fine-grained to fit into
> classes cleanly
> - enabling finer-grained code reuse
>
> Consider a method that needs to print an object. You might require a
> String:
>
> sub print_it ( String $thingie )
> {
> print $thingie;
> }
>
> Why does it have to be a String, though? What prevents it from working
> with anything that can stringify, besides the overly restrictive
> signature? What if you could say (the Perl 6 equivalent of):
>
> sub print_it ( does Stringify $thingie )
> {
> print $thingie.stringify();
> }
>
> That's both more general and something more specific. By asking for
> what you really want, you're not coding everyone else into a corner.

In this particular case I prefer the Smalltalk thing of requiring all


objects to have a C<print_on($aStream)> method and not even requiring
that C<$aStream> satisfy the Stream role. But that's probably a matter
of taste.

--
Beware the Perl 6 early morning joggers -- Allison Randal

Austin Hastings

unread,
Jan 7, 2004, 10:53:44 AM1/7/04
to chromatic, Perl6 Language

From: chromatic [mailto:chro...@wgz.org]


> On Tue, 2004-01-06 at 22:26, Austin Hastings wrote:
> > So on the grand balance of utility, what are the metrics that traits are
> > supposed to help improve?
>
> Two big ones:
>
> - naming collections of behavior that are too fine-grained
> to fit into classes cleanly
> - enabling finer-grained code reuse
>
> Consider a method that needs to print an object. You might require a
> String:
>
> sub print_it ( String $thingie )
> {
> print $thingie;
> }
>
> Why does it have to be a String, though? What prevents it from working
> with anything that can stringify, besides the overly restrictive
> signature? What if you could say (the Perl 6 equivalent of):
>
> sub print_it ( does Stringify $thingie )
> {
> print $thingie.stringify();
> }
>
> That's both more general and something more specific. By asking for
> what you really want, you're not coding everyone else into a corner.

This seems to have some pretty strong AOP implications. I can easily see
wanting to generically extend all classes with a capability (role).

So, first, can I define "inferred" roles? For example:

role Printable { requires toString(); }

such that any object which has a toString method is considered to C<does
Printable>?
If so, can I use junctions to do so? (requires
toString()|toInt()|toDouble())

Next, is there a good way to aspectify this, so that I can impose it without
having to write code to manipulate the namespace and extend every class?

class [* isa Object] but= Printable; # All children of Object are
printable

Cool, thanks.

=Austin

Austin Hastings

unread,
Jan 7, 2004, 11:43:23 AM1/7/04
to Jonathan Lang, Piers Cawley, chromatic, Perl6 Language

From: Jonathan Lang [mailto:datawe...@yahoo.com]


> Piers Cawley wrote:
> > > Why does it have to be a String, though? What prevents it from
> > > working with anything that can stringify, besides the overly
> > > restrictive signature? What if you could say (the Perl 6 equivalent
> > > of):
> > >
> > > sub print_it ( does Stringify $thingie )
> > > {
> > > print $thingie.stringify();
> > > }
> > >
> > > That's both more general and something more specific. By asking for
> > > what you really want, you're not coding everyone else into a corner.
> >
> > In this particular case I prefer the Smalltalk thing of requiring all
> > objects to have a C<print_on($aStream)> method and not even requiring
> > that C<$aStream> satisfy the Stream role. But that's probably a matter
> > of taste.
>
> Maybe as an alternative to
>
> role Stringify {must stringify();}
> sub print_it (Stringify $thingie) {print $thingie.stringify();}
>
> you might be able to say
>
> sub print_it ($thingie must stringify()) {print $thingie.stringify();}
>
> Hmm... there's a certain elegance to being able to specify one or two
> requirements directly in a signature.

Indeed. I like the idea of dynamic anonymous roles -- it's more behavioral
than anything else.

sub print_it ($thingie must stringify()) {...}

Definitely gets down to the lowest level quickly, which is nice. Even nicer
is the ability to use this sort of requirement as kind of an advanced
signature: declare exactly what you're going to do. (In other words, your
signature may say

sub foo(Object $o) {...}

because you want to accept anything in the hierarchy. But it's nice to
extend it with

sub foo(Object $o must stringify() must isa() must typeof()) {...}


This kind of granularity does kind of imply a JavaScript-like ability to
compose objects, too, no? (If you can compose requirements atomically, why
not compose capabilities, too?)

my $photon does Particle does Wave {...} = spark();

>
> Also: in the first of these two, would classof($thingie) actually have to
> have Stringify as a role, or would it be reasonable to instead say that
> classof($thingie) must meet Stringify's demands? The latter would require
> more work on the compiler's part, but would be considerably more flexible.

I prefer the latter. I want to be able to compose requirements on the way. I
certainly don't want to have to rewrite the core libraries (or extend them
all) just to mix in an interface role that they already satisfy.

> Perhaps "Stringify $thingie" requires that the Stringify role must
> actually be used, while something like "$thingie like Stringify" would
> only require that Stringify's demands be met?

My thought would be that once you have an object in hand, you can do with it
what you will. But all you get is the object. So:

(Anonymous)
sub print_it($thingie must toString()) { print $thingie.toString(); }

(Yclept)
role Stringable {
must toString();
}

sub print_it(Stringable $thingie) { print $thingie.toString(); }

Versus

(Discrete)
role Stringify {
must toString();
method stringify() { return .toString(); }
}

sub print_it(Stringify $thingie) { print $thingie.stringify(); }

The former cases don't add any methods to the objects method table, so an
"inferred" object type is okay. The latter requires that a stringify()
method be available, so you'll have to have an object that declared itself
C<does Stringify> at the outset.

=Austin

Jonathan Lang

unread,
Jan 7, 2004, 8:12:46 PM1/7/04
to Perl6 Language
Austin Hastings wrote:

> Jonathan Lang wrote:
> > Maybe as an alternative to
> >
> > role Stringify {must stringify();}
> > sub print_it (Stringify $thingie) {print $thingie.stringify();}
> >
> > you might be able to say
> >
> > sub print_it ($thingie must stringify()) {
> > print $thingie.stringify();}
> >
> > Hmm... there's a certain elegance to being able to specify one or two
> > requirements directly in a signature.
>
> Indeed. I like the idea of dynamic anonymous roles -- it's more
> behavioral
> than anything else.
>
> sub print_it ($thingie must stringify()) {...}
>
> Definitely gets down to the lowest level quickly, which is nice. Even
> nicer is the ability to use this sort of requirement as kind of an
> advanced signature: declare exactly what you're going to do. (In other
> words, your signature may say
>
> sub foo(Object $o) {...}
>
> because you want to accept anything in the hierarchy. But it's nice to
> extend it with
>
> sub foo(Object $o must stringify() must isa() must typeof()) {...}

Valid, if wordy. Roles remain useful in that they provide a more concise
way of handling this if you want it - if you've got a dozen routines that
all C<must isa() must typeof()>, you might be better off defining a role
that makes those demands, and then just use it.

> This kind of granularity does kind of imply a JavaScript-like ability to
> compose objects, too, no? (If you can compose requirements atomically,
> why not compose capabilities, too?)
>
> my $photon does Particle does Wave {...} = spark();

That's where C<but> comes in:

my $photon but does Particle does Wave {...} = spark();

would be equivelent to something like

class _anonymous_photon does Particle does Wave {...}
my _anonymous_photon $photon = spark();

> > Also: in the first of these two, would classof($thingie) actually have
> > to have Stringify as a role, or would it be reasonable to instead say
> > that classof($thingie) must meet Stringify's demands? The latter
> > would require more work on the compiler's part, but would be
> > considerably more flexible.
>
> I prefer the latter. I want to be able to compose requirements on the
> way. I certainly don't want to have to rewrite the core libraries (or
> extend them all) just to mix in an interface role that they already
> satisfy.

In principle, I agree with you; in practice, it may not be workable.

> > Perhaps "Stringify $thingie" requires that the Stringify role must
> > actually be used, while something like "$thingie like Stringify" would
> > only require that Stringify's demands be met?
>
> My thought would be that once you have an object in hand, you can do
> with it what you will. But all you get is the object.

How would you handle the following:

role Dog {must bark();}
role Tree {must bark();}

class crossPerson {
method bark() {speak_sharply;}
}

class Trog does Tree does Dog {
method bark() {bark_like_a_trog;}
}

multi sub purchase(Dog $mansBestFriend) {...}
multi sub purchase(Tree $shrubbery) {...}
multi sub purchase($noisemaker must bark()) {...}

my crossPerson $jack;
purchase $jack;

my Trog $spot;
purchase $spot;

Which, if any, of the subs should be called in each case? Or should the
compiler complain of duplicate definitions?

Chromatic

unread,
Jan 8, 2004, 12:34:35 AM1/8/04
to Jonathan Lang, Perl6 Language
On Wed, 2004-01-07 at 00:43, Jonathan Lang wrote:

> Maybe as an alternative to
>
> role Stringify {must stringify();}
> sub print_it (Stringify $thingie) {print $thingie.stringify();}
>
> you might be able to say
>
> sub print_it ($thingie must stringify()) {print $thingie.stringify();}
>
> Hmm... there's a certain elegance to being able to specify one or two
> requirements directly in a signature.

I'm not sure that works so well in practice. You're explicitly asking
for a method with a particular name when you're ought to be asking for a
method with a particular meaning. That is, if you said:

method threaten ( $thingie must bark() ) { ... }

passing in a Tree object would work, when what you really want something
that does Doggish things, like a Dog, an Actor in a dog costume, or a
RobotDog.

Promoting role names to a position of typishness allows roles to express
the semantics and context of method names that method names alone can't
express uniquely.

Yikes, now I sound like Larry.

<Larry>Or maybe not.</Larry>

-- c

Austin Hastings

unread,
Jan 8, 2004, 2:28:23 AM1/8/04
to chromatic, Perl6 Language
From: chromatic [mailto:chro...@wgz.org]

> On Wed, 2004-01-07 at 00:43, Jonathan Lang wrote:
> > Maybe as an alternative to
> >
> > role Stringify {must stringify();}
> > sub print_it (Stringify $thingie) {print $thingie.stringify();}
> >
> > you might be able to say
> >
> > sub print_it ($thingie must stringify()) {print
> $thingie.stringify();}
> >
> > Hmm... there's a certain elegance to being able to specify one or two
> > requirements directly in a signature.
>
> I'm not sure that works so well in practice. You're explicitly asking
> for a method with a particular name when you're ought to be asking for a
> method with a particular meaning. That is, if you said:
>
> method threaten ( $thingie must bark() ) { ... }
>
> passing in a Tree object would work, when what you really want something
> that does Doggish things, like a Dog, an Actor in a dog costume, or a
> RobotDog.

Ahh, this is the classic Damian interpretation.

Yes, passing in a Tree would satisfy the requirement. That's the intention.
If we wanted to say that the requirement was for a Dog role, we'd say that.
Instead, we're being very generic, and saying that anything with a bark()
method is valid fodder for this.

For example,

sub bark_louder($it must bark()) {
System::Audio::set_volume(+5);
$it.bark;
System::Audio::set_volume(-5);
}

> Promoting role names to a position of typishness allows roles to express
> the semantics and context of method names that method names alone can't
> express uniquely.
>
> Yikes, now I sound like Larry.

Sure, but you're missing the point. It's not "methods instead of roles",
it's "methods when even roles are too abstract."

=Austin

0 new messages