Vocabulary

16 views
Skip to first unread message

Luke Palmer

unread,
Dec 12, 2003, 6:23:02 AM12/12/03
to Language List
So I'm seeing a lot of inconsistent OO-vocabulary around here, and it
makes things pretty hard to understand.

So here's how Perl 6 is using said inconsistent terms, AFAIK:

- attribute
A concrete data member of a class. Used with C<has>.

- property
An out-of-band sticky note to be placed on a single object.
Used with C<but>.

- trait
A compile time sticky note to be placed on a wide variety of things.
Used with C<is>.

- role
A collection of methods to be incorporated into a class sans
inheritance (and maybe some other stuff, too). Used with C<does>.

So for example:

class Dog
does Boolean # role
is extended # trait
is Mammal # [1]
{
has $.tail; # attribute
has @.legs; # attribute
}

my $fido = Dog.new
but false; # property

Hope that clears things up.

Luke

[1] This is a base class, which is an overloaded use of C<is>. Though,
upon A12 release, we'll probably find out that it's not overloaded but
instead, elegantly unified, somehow.

Jonathan Scott Duff

unread,
Dec 12, 2003, 11:13:16 AM12/12/03
to Luke Palmer, Language List
On Fri, Dec 12, 2003 at 04:23:02AM -0700, Luke Palmer wrote:
> So I'm seeing a lot of inconsistent OO-vocabulary around here, and it
> makes things pretty hard to understand.
>
> So here's how Perl 6 is using said inconsistent terms, AFAIK:
>
> - attribute
> A concrete data member of a class. Used with C<has>.
>
> - property
> An out-of-band sticky note to be placed on a single object.
> Used with C<but>.

I think an important aspect of properties that you left out here is
that they are run-time.

> - trait
> A compile time sticky note to be placed on a wide variety of things.
> Used with C<is>.
>
> - role
> A collection of methods to be incorporated into a class sans
> inheritance (and maybe some other stuff, too). Used with C<does>.

s/class/object/

Roles are like sticky behavior (as I understand them) just as properties
are sticky state. And I assume that roles are run-time so that you can
have your objects obtain new behavior (fullfill new roles) as needed
without having to use eval all the time.

I think I'm getting it but I'm not sure. Does something like this
work?

my role Teach { ... }
my role Operate { ... }
my role Learn { ... }

my Person $frank;
{ temp $frank_the_teacher = $frank does Teach; ... }
{ temp $frank_the_doctor = $frank does Operate; ... }
{ temp $frank_the_student = $frank does Learn; ... }

I.e., we can use dynamic scoping to control how long an object
fulfills a particular role? Maybe it could also be written like so:

my Person $frank;
{ my role Teach { ... }; $frank does Teach; ... }
{ my role Operate { ... }; $frank does Operate; ... }
{ my role Learn { ... } $frank does Learn; ... }

so that when the role goes out of scope, the object no longer
possesses the abilities of that role.

I confuse myself everytime I think about this stuff.

-Scott
--
Jonathan Scott Duff
du...@lighthouse.tamucc.edu

Larry Wall

unread,
Dec 12, 2003, 12:16:38 PM12/12/03
to Language List
On Fri, Dec 12, 2003 at 04:23:02AM -0700, Luke Palmer wrote:
: So I'm seeing a lot of inconsistent OO-vocabulary around here, and it

: makes things pretty hard to understand.

Agreed.

: So here's how Perl 6 is using said inconsistent terms, AFAIK:


:
: - attribute
: A concrete data member of a class. Used with C<has>.

Declared with C<has> is a little more precise.

: - property


: An out-of-band sticky note to be placed on a single object.
: Used with C<but>.

Maybe "applied with"?

: - trait


: A compile time sticky note to be placed on a wide variety of things.
: Used with C<is>.

Fine. (Though I like to hyphenate "compile-time" when it's an adjective,
and not when it's a noun. Same for "run-time", just to be consistent.)

: - role


: A collection of methods to be incorporated into a class sans

A role can also supply one or more attributes.

: inheritance (and maybe some other stuff, too). Used with C<does>.

Here it gets a little fuzzier. A role can be applied to a class
at compile time via "does", or to an object at run time via "but".
A property is a simple kind of role that supplies a single attribute.
The type of a property is identical to its role name. Roles can have
subtypes that function as enums when the subtypes are constrained to a
single value. You can use one of these subtypes without specifically
implying the role name. So saying

$bar but Red

might give you a value with the property Color. You can write the corresponding
boolean test using the smart match operator:

$bar ~~ Red

and it (smartly) picks out the Color property to compare with, provided
it's unambiguous. You can use that syntax to compare against any
subtype or junction of subtypes:

$bar ~~ Redish&Whiteish # pinkish

: So for example:


:
: class Dog
: does Boolean # role
: is extended # trait
: is Mammal # [1]
: {
: has $.tail; # attribute
: has @.legs; # attribute
: }
:
: my $fido = Dog.new
: but false; # property
:
: Hope that clears things up.

Yes, it does.

: Luke


:
: [1] This is a base class, which is an overloaded use of C<is>. Though,
: upon A12 release, we'll probably find out that it's not overloaded but
: instead, elegantly unified, somehow.

If not, it'll be easy to turn it into an "isa".

Larry

Austin Hastings

unread,
Dec 12, 2003, 4:27:59 PM12/12/03
to du...@lighthouse.tamucc.edu, Perl6 Language

This seems needlessly restrictive. If we're defining roles as having mixin
capabilities, we should be able to use them in classes. Laziness.

role Work {...};
role Management {...};

class Employee {...};

class Worker is Employee does Work;
class Manager is Employee does Management;

role PHB does Manager[does no Work];

> I think I'm getting it but I'm not sure. Does something like this
> work?
>
> my role Teach { ... }
> my role Operate { ... }
> my role Learn { ... }
>
> my Person $frank;
> { temp $frank_the_teacher = $frank does Teach; ... }
> { temp $frank_the_doctor = $frank does Operate; ... }
> { temp $frank_the_student = $frank does Learn; ... }
>
> I.e., we can use dynamic scoping to control how long an object
> fulfills a particular role? Maybe it could also be written like so:
>
> my Person $frank;
> { my role Teach { ... }; $frank does Teach; ... }
> { my role Operate { ... }; $frank does Operate; ... }
> { my role Learn { ... } $frank does Learn; ... }
>
> so that when the role goes out of scope, the object no longer
> possesses the abilities of that role.
>
> I confuse myself everytime I think about this stuff.

That's brilliant, if twisted. The object persists, but the behaviors expire.
There's a paradigm there, man. Write a book.

(It's double-e Eevil, too, but that's Damian's problem. :-)

=Austin

Austin Hastings

unread,
Dec 12, 2003, 5:17:37 PM12/12/03
to Language List

> -----Original Message-----
> From: Larry Wall [mailto:la...@wall.org]
> Sent: Friday, December 12, 2003 12:17 PM

> : - role
> : A collection of methods to be incorporated into a class sans
>
> A role can also supply one or more attributes.

So a role can constrain values and add behavior and attributes. Presumably
it can do both at the same time?

enum ParityMode values <P_ODD P_EVEN P_NONE>;

role Byte
does Int[0..255] # Value constraint
does { # extending by adding attributes & methods, and by
overriding the STORE method
has ParityMode $.parity_mode = NONE;
has bit $.parity;

# .CONFORM is redundant with Value constraint above,
which autogenerates this.
method CONFORM(Int $i) { SUPER && 0 <= $i <= 255; }
method STORE(Int $i: $v) { $i = .CONFORM($v) || fail; set_parity; }
method set_parity {...}
};

> : inheritance (and maybe some other stuff, too). Used
> with C<does>.
>
> Here it gets a little fuzzier. A role can be applied to a class
> at compile time via "does", or to an object at run time via "but".

Good. I like the mixin being available at either time. This makes properties
a lot more useful since I can provided "default" or "normal" values:

role Celebrated
does Date
does {
method celebrated($d) { return $d.date; }
}

class Birthday does Celebrated {
has $.date;
}

my Birthday $d = Birthday.new('February', 29, 2004) but
Celebrated('March', 01, 2004);

print "My birthday is celebrated $d.celebrated";

I presume that the linear order (compile time) or chronological order of
applying roles decides the order in which overlaid methods are
C<wrap>ed/overlaid.

Which is it, by the way? Or is there MTOWTDI, such as a method modifier for
specifying polymorph behavior?

method CONFORM is wrapped { ... call ... }

> A property is a simple kind of role that supplies a single attribute.
> The type of a property is identical to its role name. Roles can have
> subtypes that function as enums when the subtypes are constrained to a
> single value.

This seems really clunky for enums. It works okay for boolean, but even
doing month-names is going to suck pretty hard:

role Month;

role January does Month[0];
role February does Month[1];
role March does Month[2];
role April does Month[3];
role May does Month[4];
role June does Month[5];
role July does Month[6];
role August does Month[7];
role September does Month[8];
role October does Month[9];
role November does Month[10];
role December does Month[11];

role Month does Int[January..December];

> You can use one of these subtypes without specifically implying the role
name. So saying
>
> $bar but Red
>
> might give you a value with the property Color.

This is smart and helpful. I like it. However, there needs to be a way to
specify what to do when multiple roles share the same values. For example,
if I have NeededBy and Estimated roles:

my $birthday = "02/29/2004" but March;
my $ship_date = "01/01/01" but NeededBy(February);

> You can write the corresponding boolean test using the smart match
operator:
>
> $bar ~~ Red
>
> and it (smartly) picks out the Color property to compare with, provided
> it's unambiguous. You can use that syntax to compare against any
> subtype or junction of subtypes:
>
> $bar ~~ Redish&Whiteish # pinkish
>

Disambiguation?

$bar ~~ NeededBy(February)

or

$bar.NeededBy ~~ February

=Austin

Stéphane Payrard

unread,
Dec 12, 2003, 1:12:40 PM12/12/03
to Larry Wall, perl6-l...@perl.org
>
> A role can also supply one or more attributes.
>
> : inheritance (and maybe some other stuff, too). Used with C<does>.

The smalltalk paper you mentionned which talked about roles (under
the name of traits) said that roles were stateless.

What are the consequences of using stateful roles?

A related question. Will getter and setter methods will have the
same name as the underlying accessed attributes?


--
stef

Dan Sugalski

unread,
Dec 12, 2003, 1:36:37 PM12/12/03
to Larry Wall, Language List
At 9:16 AM -0800 12/12/03, Larry Wall wrote:
>On Fri, Dec 12, 2003 at 04:23:02AM -0700, Luke Palmer wrote:
>: - property
>: An out-of-band sticky note to be placed on a single object.
>: Used with C<but>.
>
>Maybe "applied with"?
>
>: - trait
>: A compile time sticky note to be placed on a wide variety of things.
>: Used with C<is>.
>
>Fine. (Though I like to hyphenate "compile-time" when it's an adjective,
>and not when it's a noun. Same for "run-time", just to be consistent.)

I would really, *really* like to kill the whole "It's a sticky note!"
metaphor dead. If I understand the changes proposed in properties as
part of the whole "shift to roles" thing they aren't anything like
sticky notes at all, as they dynamically subclass the object.
--
Dan

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

Larry Wall

unread,
Dec 12, 2003, 7:06:52 PM12/12/03
to Perl6 Language
On Fri, Dec 12, 2003 at 04:27:59PM -0500, Austin Hastings wrote:
: > -----Original Message-----

: > From: Jonathan Scott Duff [mailto:du...@lighthouse.tamucc.edu]
: > I think I'm getting it but I'm not sure. Does something like this

: > work?
: >
: > my role Teach { ... }
: > my role Operate { ... }
: > my role Learn { ... }
: >
: > my Person $frank;
: > { temp $frank_the_teacher = $frank does Teach; ... }
: > { temp $frank_the_doctor = $frank does Operate; ... }
: > { temp $frank_the_student = $frank does Learn; ... }
: >
: > I.e., we can use dynamic scoping to control how long an object
: > fulfills a particular role? Maybe it could also be written like so:
: >
: > my Person $frank;
: > { my role Teach { ... }; $frank does Teach; ... }
: > { my role Operate { ... }; $frank does Operate; ... }
: > { my role Learn { ... } $frank does Learn; ... }
: >
: > so that when the role goes out of scope, the object no longer
: > possesses the abilities of that role.
: >
: > I confuse myself everytime I think about this stuff.
:
: That's brilliant, if twisted. The object persists, but the behaviors expire.
: There's a paradigm there, man. Write a book.

The behavior probably doesn't expire unless you've cloned the object
and the clone expires. However, if a role goes out of its lexical
scope, it can't be named, so it's effectively not usable unless you
dig out a name for it via reflection. But the information is still
cached there, so the object could be smarter the next time it takes
on the same role.

That being said, a role applied with C<temp> probably *should* be
stripped out when it goes out of scope. Could get messy though...

Larry

Larry Wall

unread,
Dec 12, 2003, 7:24:19 PM12/12/03
to perl6-l...@perl.org, Stéphane Payrard
On Fri, Dec 12, 2003 at 07:12:40PM +0100, Stéphane Payrard wrote:
: >
: > A role can also supply one or more attributes.

: >
: > : inheritance (and maybe some other stuff, too). Used with C<does>.
:
: The smalltalk paper you mentionned which talked about roles (under
: the name of traits) said that roles were stateless.

Though they did point out that state was one of the thing they
wanted to look into.

: What are the consequences of using stateful roles?

That's what we're trying to figure out. :-)

It seems to me that as long as the attributes declared in the role are
elaborated into the "real" object at class composition time, there's
really very little problem with doing that part of it. You have to
watch for collisions just as you do with method names, of course.
The tricky part comes later when you start to use it. The trickiest
thing will be to know if some change in the rest of the object has
invalidated your role's state such that you have to recompute it.

: A related question. Will getter and setter methods will have the


: same name as the underlying accessed attributes?

Yes, the getter/setter method is the same name as the attribute.
(There's only one method, but it can be an lvalue method for rw
attributes).

Larry

Larry Wall

unread,
Dec 12, 2003, 8:30:07 PM12/12/03
to Language List
On Fri, Dec 12, 2003 at 05:17:37PM -0500, Austin Hastings wrote:
: > -----Original Message-----

: > From: Larry Wall [mailto:la...@wall.org]
: > Sent: Friday, December 12, 2003 12:17 PM
:
: > : - role
: > : A collection of methods to be incorporated into a class sans
: >
: > A role can also supply one or more attributes.
:
: So a role can constrain values and add behavior and attributes. Presumably
: it can do both at the same time?

I suspect so. Some added behaviors may only make sense on a constrained
set of values.

: enum ParityMode values <P_ODD P_EVEN P_NONE>;


:
: role Byte
: does Int[0..255] # Value constraint
: does { # extending by adding attributes & methods, and by
: overriding the STORE method
: has ParityMode $.parity_mode = NONE;
: has bit $.parity;
:
: # .CONFORM is redundant with Value constraint above,
: which autogenerates this.
: method CONFORM(Int $i) { SUPER && 0 <= $i <= 255; }
: method STORE(Int $i: $v) { $i = .CONFORM($v) || fail; set_parity; }
: method set_parity {...}

: };

Yes, though CONFORM is likely to be spelled PRE.

And I'm not sure your STORE is gonna work by clobbering the invocant
reference like that. More likely you have to assign to .value or some
such accessor provided by Int. Or since roles compose into the class,
it may be okay for roles to access attribute variables directly,
and set $.value (presuming that's the attribute provided by Int).
Depends on how fancy we want to get with the cross checking at
composition time.

: > : inheritance (and maybe some other stuff, too). Used


: > with C<does>.
: >
: > Here it gets a little fuzzier. A role can be applied to a class
: > at compile time via "does", or to an object at run time via "but".
:
: Good. I like the mixin being available at either time. This makes properties
: a lot more useful since I can provided "default" or "normal" values:
:
: role Celebrated
: does Date
: does {
: method celebrated($d) { return $d.date; }
: }
:
: class Birthday does Celebrated {
: has $.date;
: }
:
: my Birthday $d = Birthday.new('February', 29, 2004) but
: Celebrated('March', 01, 2004);
:
: print "My birthday is celebrated $d.celebrated";

More generally, you can write the rest of the class knowing that the
role is there if it's compiled in.

: I presume that the linear order (compile time) or chronological order of


: applying roles decides the order in which overlaid methods are
: C<wrap>ed/overlaid.

The original Traits paper specifies that it's illegal to compose two
methods of the same name into the class, and you have to rename one of
them to get them both visible. This is why the authors specifically
rejected mixins, because they hide errors like this.

As for the relationship of "Trait" methods to other methods
declarations, an explicit method declaration in the class proper
overrides the composed methods, while composed methods override
anything else in the inheritance hierarchy.

: Which is it, by the way? Or is there MTOWTDI, such as a method modifier for
: specifying polymorph behavior?

The default way might well be the way specified in the Traits paper.
However, their underlying language didn't support any kind of multi
dispatch. Perl 6 will be able to "multi" any set of names in the same
namespace as long as the arguments are differentiable by type. So it
might be possible to insert a stub method declaration in the class
proper that says "treat all composed methods of this name as multis".
That presumes the methods take differing arguments, of course.

: method CONFORM is wrapped { ... call ... }

That would be another way to do it, except that you might still have
to switch on something to tell it which role method to call.

: > A property is a simple kind of role that supplies a single attribute.


: > The type of a property is identical to its role name. Roles can have
: > subtypes that function as enums when the subtypes are constrained to a
: > single value.
:
: This seems really clunky for enums. It works okay for boolean, but even
: doing month-names is going to suck pretty hard:
:
: role Month;
:
: role January does Month[0];
: role February does Month[1];
: role March does Month[2];
: role April does Month[3];
: role May does Month[4];
: role June does Month[5];
: role July does Month[6];
: role August does Month[7];
: role September does Month[8];
: role October does Month[9];
: role November does Month[10];
: role December does Month[11];
:
: role Month does Int[January..December];

That's why I suggested some syntactic sugar for it. But I admit that
treating each enum as a subtype is a stretch. They could be constant
methods, for instance. In any event, the various enum names should
probably be hidden in the Month role and not be exported by default.

: > You can use one of these subtypes without specifically implying the role


: name. So saying
: >
: > $bar but Red
: >
: > might give you a value with the property Color.
:
: This is smart and helpful. I like it. However, there needs to be a way to
: specify what to do when multiple roles share the same values. For example,
: if I have NeededBy and Estimated roles:
:
: my $birthday = "02/29/2004" but March;
: my $ship_date = "01/01/01" but NeededBy(February);

Conflicts within a class either need to be reported as soon as they're
spotted, or dealt with by some kind of multi dispatch. There's no hiding
or overriding within a composed class.

: > You can write the corresponding boolean test using the smart match


: operator:
: >
: > $bar ~~ Red
: >
: > and it (smartly) picks out the Color property to compare with, provided
: > it's unambiguous. You can use that syntax to compare against any
: > subtype or junction of subtypes:
: >
: > $bar ~~ Redish&Whiteish # pinkish
: >
:
: Disambiguation?
:
: $bar ~~ NeededBy(February)
:
: or
:
: $bar.NeededBy ~~ February

Certainly the second form should work, unless it blows up because
there's no NeededBy property. Not sure about the parens on the first
form, but there has to be some syntax that makes the type of an enum
explicit, and that form is probably going to be preferred if the
second form blows up on non-existent properties. And the syntax
needs to support something like

NeededBy[January|July]

so NeededBy::January is probably not going to cut it.

Larry

Austin Hastings

unread,
Dec 13, 2003, 12:07:40 PM12/13/03
to Perl6 Language
> From: Larry Wall [mailto:la...@wall.org]
>

It's a role closure, in other words?

That being the case, how to you unapply a role?

$frank does no Teach;

$frank doesnt Teach;


> That being said, a role applied with C<temp> probably *should* be
> stripped out when it goes out of scope. Could get messy though...

I can't think of a way to apply a role with temp (to a non-temp object). How
do you do it?

=Austin

Austin Hastings

unread,
Dec 13, 2003, 12:50:50 PM12/13/03
to Language List

I'm not convinced these are errors. Having a role override methods makes
sense in a lot of ways.

Consider, for example, a caching or persistence implementation that
overrides the .STORE method of its victims.

It seems to me there's an argument both ways --

1. Code written in the absence of a role won't anticipate the role and
therefore won't take (unknowable) steps to disambiguate method calls. Ergo
method overloads are bad.

2. Roles may be written to deliberately supercede the methods of their
victims. Method overloads are vital.

This doesn't take into account role vs. role conflicts (which seem more
likely to be serendipitous).

Perhaps an "exact signature" rule would work? Such that if the method was an
exact replacement, then no error occurs, otherwise it becomes either an
error or a multi?

Alternatively, perhaps a role must declare its method to be multi or not,
and if not then the role's method overloads the original class's.

(Which takes us to retroactive multi-fication. Ugh.)

Or perhaps you just have to say "this method overloads".

> As for the relationship of "Trait" methods to other methods
> declarations, an explicit method declaration in the class proper
> overrides the composed methods, while composed methods override
> anything else in the inheritance hierarchy.

At compile time, right? Whereas composition (but=) overrides declaration at
run-time?

Yeah, the concept is useful enough that it's probably worth a spoonful of
sugar. Perhaps it were better to think of a clever way of defining a batch
of named constants in a class declaration, so that enums could be full
classes if they want to be:

class Month is Int {
method name {...};
has values [ January => 0, February, ..., December ];
}

> : > You can use one of these subtypes without specifically
> implying the role
> : name. So saying
> : >
> : > $bar but Red
> : >
> : > might give you a value with the property Color.
> :
> : This is smart and helpful. I like it. However, there needs to
> be a way to
> : specify what to do when multiple roles share the same values.
> For example,
> : if I have NeededBy and Estimated roles:
> :
> : my $birthday = "02/29/2004" but March;
> : my $ship_date = "01/01/01" but NeededBy(February);
>
> Conflicts within a class either need to be reported as soon as they're
> spotted, or dealt with by some kind of multi dispatch. There's no hiding
> or overriding within a composed class.

This isn't hiding/overriding directly, but resolving an ambiguous value
(February) which could belong to either the NeededBy or the Estimated roles.

Consider C<green> -- does it mean Color(green) or Ripeness(green)?

> : > You can write the corresponding boolean test using the smart match
> : operator:
> : >
> : > $bar ~~ Red
> : >
> : > and it (smartly) picks out the Color property to compare
> with, provided
> : > it's unambiguous. You can use that syntax to compare against any
> : > subtype or junction of subtypes:
> : >
> : > $bar ~~ Redish&Whiteish # pinkish
> : >
> :
> : Disambiguation?
> :
> : $bar ~~ NeededBy(February)
> :
> : or
> :
> : $bar.NeededBy ~~ February
>
> Certainly the second form should work, unless it blows up because
> there's no NeededBy property. Not sure about the parens on the first
> form, but there has to be some syntax that makes the type of an enum
> explicit, and that form is probably going to be preferred if the
> second form blows up on non-existent properties. And the syntax
> needs to support something like
>
> NeededBy[January|July]
>
> so NeededBy::January is probably not going to cut it.

Two good points.

=Austin

Larry Wall

unread,
Dec 13, 2003, 2:26:52 PM12/13/03
to Perl6 Language
On Sat, Dec 13, 2003 at 12:07:40PM -0500, Austin Hastings wrote:
: > From: Larry Wall [mailto:la...@wall.org]
: > The behavior probably doesn't expire unless you've cloned the object

: > and the clone expires. However, if a role goes out of its lexical
: > scope, it can't be named, so it's effectively not usable unless you
: > dig out a name for it via reflection. But the information is still
: > cached there, so the object could be smarter the next time it takes
: > on the same role.
:
: It's a role closure, in other words?

Erm. That's a fancy word, and I don't claim to know what it means
all the time. I suspect the name of the role is closed but the role
itself isn't. Alice: "If the name of the role is called Teach..."

: That being the case, how to you unapply a role?


:
: $frank does no Teach;
:
: $frank doesnt Teach;

$frank.role_manager(
action => "delete",
mode => "override_all_safety_mechanisms",
name_the_role_is_called => "Teach"
);

Or something like that. :-)

: > That being said, a role applied with C<temp> probably *should* be


: > stripped out when it goes out of scope. Could get messy though...
:
: I can't think of a way to apply a role with temp (to a non-temp object). How
: do you do it?

Well, we did set up a way for a method to be temporizable, so it
probably comes down to whether C<but> is just syntactic sugar for a
method call that knows how to undo itself.

Larry

Larry Wall

unread,
Dec 13, 2003, 4:44:34 PM12/13/03
to Language List
On Sat, Dec 13, 2003 at 12:50:50PM -0500, Austin Hastings wrote:
: > -----Original Message-----

: > From: Larry Wall [mailto:la...@wall.org]
: > Sent: Friday, December 12, 2003 8:30 PM
:
: > On Fri, Dec 12, 2003 at 05:17:37PM -0500, Austin Hastings wrote:
: > : I presume that the linear order (compile time) or chronological order of

: > : applying roles decides the order in which overlaid methods are
: > : C<wrap>ed/overlaid.
: >
: > The original Traits paper specifies that it's illegal to compose two
: > methods of the same name into the class, and you have to rename one of
: > them to get them both visible. This is why the authors specifically
: > rejected mixins, because they hide errors like this.
:
: I'm not convinced these are errors. Having a role override methods makes
: sense in a lot of ways.

A role method certainly overrides inherited methods, so it's only
methods defined in the class itself we're talking about here.

: Consider, for example, a caching or persistence implementation that


: overrides the .STORE method of its victims.

I think the class is still the final arbiter of what its objects
are--there is no other entity that holds all the reins. If a class
chooses to include a role, and that role violates the normal rules of
roles, the class is still responsible for that (or else you need some
babysitting code somewhere, hopefully in the metaclass). Maybe that's
what a trait is--a renegade role. Instead of

does Storable

maybe it's

is storable

: It seems to me there's an argument both ways --


:
: 1. Code written in the absence of a role won't anticipate the role and
: therefore won't take (unknowable) steps to disambiguate method calls. Ergo
: method overloads are bad.
:
: 2. Roles may be written to deliberately supercede the methods of their
: victims. Method overloads are vital.

I think the default has to be 1, with an explicit way to get 2, preferably
with the agreement of the class in question, though that's not absolutely
necessary if you believe in AOP.

: This doesn't take into account role vs. role conflicts (which seem more


: likely to be serendipitous).
:
: Perhaps an "exact signature" rule would work? Such that if the method was an
: exact replacement, then no error occurs, otherwise it becomes either an
: error or a multi?

Er, which method?

: Alternatively, perhaps a role must declare its method to be multi or not,


: and if not then the role's method overloads the original class's.

No, I think the default needs to be such that the class's method is
expected to dispatch to the role's method. If no such method exists
then it falls back on the normal role method dispatch. In either case,
it would almost always be the case that you'd want multimethod dispatch
to the set of role methods of the same name.

I'm starting to think that any methods declared in roles are automatically
considered "multi" when composed, whether so declared or not.

: (Which takes us to retroactive multi-fication. Ugh.)

More like proactive multi-fication, I think.

: Or perhaps you just have to say "this method overloads".

If it's part of the role contract, it's part of the contract. But maybe
that makes it a trait.

: > As for the relationship of "Trait" methods to other methods


: > declarations, an explicit method declaration in the class proper
: > overrides the composed methods, while composed methods override
: > anything else in the inheritance hierarchy.
:
: At compile time, right? Whereas composition (but=) overrides declaration at
: run-time?

I don't think the rules for run-time roles should be different than the
rules for compile-time roles (because Perl doesn't make a hard-and-fast
distinction between compile time and run time). And the arbitration
logic has to be somehow associated with the class, either explicitly
by the class's declarations, or by some babysitting code telling the
class how to behave given the new composition.

I've been talking about singleton classes to implement run-time
roles, but that's not quite right. I think a class caches its
various compositions and either reuses an existing one or creates
a new composition depending on which set of roles has been bound to
the current object. It might seem like you could have a combinatorial
explosion of the possible number of compositions if multiple properties
are applied at run time, but when you think about, the number of those
cached compositions has to be equal or less than the number of singleton
classes you'd get if you created a new one for every object with one
or more properties. In general there will be many fewer compositions
than singletons.

So whenever you bind a run-time role, the class looks to see if it
already knows how to do the combination of roles this object wants,
and if so, the role binding is very fast. Otherwise it creates the new
composition, checks for conflicts, resolves them (or doesn't), and then
binds the new composition as the object's current view of its class.

In a sense, these are the true mixins. The things people normally call
"mixins" are more like "slatherons".

Larry

Jonathan Scott Duff

unread,
Dec 14, 2003, 4:16:16 AM12/14/03
to Language List
On Sat, Dec 13, 2003 at 01:44:34PM -0800, Larry Wall wrote:
> On Sat, Dec 13, 2003 at 12:50:50PM -0500, Austin Hastings wrote:
> : It seems to me there's an argument both ways --
> :
> : 1. Code written in the absence of a role won't anticipate the role and
> : therefore won't take (unknowable) steps to disambiguate method calls. Ergo
> : method overloads are bad.
> :
> : 2. Roles may be written to deliberately supercede the methods of their
> : victims. Method overloads are vital.
>
> I think the default has to be 1, with an explicit way to get 2, preferably
> with the agreement of the class in question, though that's not absolutely
> necessary if you believe in AOP.

So, if we follow the rules in the Traits paper, a role may have no
semantic effect if the object's class already provides the necessary
methods. To *guarantee* that a role will modify an object's behavior,
we need some sytactic clue. Perhaps "shall"?

my Person $pete shall Work;

Whatever methods Work defines will override corresponding methods of the
same name (signature?) in the Person class. (With "will" or "does" just
the opposite is true) And that same idea could extend to roles
overriding roles easy enough:

my Person $pete will Work shall Lead;
$pete.order(); # calls Lead.order

i.e. if the class Person, and the roles Work and Lead all define a
method called order(), then the Lead role's order() will be the one
called.

I'm not sure that "will" works that way, but you get the idea.

WRT to the classes cooperation, should a class be able to say "You
can't override this method"?

method foo is forever { ... }

Seems like it would make it harder for the programmers to debug in the
event of problems. I guess that could be mitigated by clear error
reporting and rampant object introspection.

> : This doesn't take into account role vs. role conflicts (which seem more
> : likely to be serendipitous).
> :
> : Perhaps an "exact signature" rule would work? Such that if the method was an
> : exact replacement, then no error occurs, otherwise it becomes either an
> : error or a multi?
>
> Er, which method?
>
> : Alternatively, perhaps a role must declare its method to be multi or not,
> : and if not then the role's method overloads the original class's.
>
> No, I think the default needs to be such that the class's method is
> expected to dispatch to the role's method. If no such method exists
> then it falls back on the normal role method dispatch. In either case,
> it would almost always be the case that you'd want multimethod dispatch
> to the set of role methods of the same name.
>
> I'm starting to think that any methods declared in roles are automatically
> considered "multi" when composed, whether so declared or not.

Hmm. So that would mean that we'd need a syntax for method replacement
when we wanted it (which we'd need anyway if the method was our smallest
unit of reuse rather than the role) and the only time we would get an
error is when 2 (or more) roles included in a composition have exactly
the same signature. [slight digression, but methods are really
singleton roles aren't they?]

I'm not sure now whether I like idea of "shall" at all now. It seems
better to just have every method of a role declare that it replaces the
same-name method of the class. If the class doesn't want future roles
replacing a given method, then we get an error. Oh, but without "shall",
composition order would matter and there'd be no visual cue at
composition time that a particular role is special. Drat.

Surely we would need a way to do same-signature replacement on methods
too? This would mildly argue against an implicit "multi".

> So whenever you bind a run-time role, the class looks to see if it
> already knows how to do the combination of roles this object wants,
> and if so, the role binding is very fast. Otherwise it creates the new
> composition, checks for conflicts, resolves them (or doesn't), and then
> binds the new composition as the object's current view of its class.

Neat.

Piers Cawley

unread,
Dec 14, 2003, 6:41:39 AM12/14/03
to Language List
Jonathan Scott Duff <du...@lighthouse.tamucc.edu> writes:

> On Sat, Dec 13, 2003 at 01:44:34PM -0800, Larry Wall wrote:
>> On Sat, Dec 13, 2003 at 12:50:50PM -0500, Austin Hastings wrote:
>> : It seems to me there's an argument both ways --
>> :
>> : 1. Code written in the absence of a role won't anticipate the role and
>> : therefore won't take (unknowable) steps to disambiguate method calls. Ergo
>> : method overloads are bad.
>> :
>> : 2. Roles may be written to deliberately supercede the methods of their
>> : victims. Method overloads are vital.
>>
>> I think the default has to be 1, with an explicit way to get 2, preferably
>> with the agreement of the class in question, though that's not absolutely
>> necessary if you believe in AOP.
>
> So, if we follow the rules in the Traits paper, a role may have no
> semantic effect if the object's class already provides the necessary
> methods. To *guarantee* that a role will modify an object's behavior,
> we need some sytactic clue. Perhaps "shall"?
>
> my Person $pete shall Work;

But presumably

my Person $self will Work;

Using a pair of words that change their meaning depending on the
subject of the verb seems to be a courageous choice of language and
rather too contextual even for Perl.

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

Larry Wall

unread,
Dec 14, 2003, 9:14:42 PM12/14/03
to Language List
On Sun, Dec 14, 2003 at 03:16:16AM -0600, Jonathan Scott Duff wrote:
: So, if we follow the rules in the Traits paper, a role may have no

: semantic effect if the object's class already provides the necessary
: methods. To *guarantee* that a role will modify an object's behavior,
: we need some sytactic clue. Perhaps "shall"?
:
: my Person $pete shall Work;
:
: Whatever methods Work defines will override corresponding methods of the
: same name (signature?) in the Person class. (With "will" or "does" just
: the opposite is true) And that same idea could extend to roles
: overriding roles easy enough:
:
: my Person $pete will Work shall Lead;
: $pete.order(); # calls Lead.order
:
: i.e. if the class Person, and the roles Work and Lead all define a
: method called order(), then the Lead role's order() will be the one
: called.
:
: I'm not sure that "will" works that way, but you get the idea.

I think there's a simple way to solve this: If you're changing the
policy of the class, then you're changing the class! Derive from the
"defective" class and pull in the roles the way you prefer. If we're
taking away the job of code reuse away from classes and giving it to
roles, then the only job left to classes is object policy. Let's not
take that away too.

: WRT to the classes cooperation, should a class be able to say "You


: can't override this method"?
:
: method foo is forever { ... }
:
: Seems like it would make it harder for the programmers to debug in the
: event of problems. I guess that could be mitigated by clear error
: reporting and rampant object introspection.

I'm deeply suspicious of any trait resembling "final". The way
to prevent people from overriding your interface is to write the
interface good enough that no one will want to override it. Good
luck. :-)

[begin digression]

The other reason for "final" is to make it easy for the compiler
to optimize. That's also problematical. As implemented by Java,
it's a premature optimization. The point at which you'd like to
know this sort of thing is just after parsing the entire program and
just before code generation. And the promises have to come from
the users of interfaces, not the providers, because the providers
don't know how their services are going to be used. Methods, roles,
and classes may never declare themselves final. They may be declared
final only by the agreement of all their users.

But the agreement could be implied by silence. If, by the time the
entire program is parsed, nobody has said they want to extend an
interface, then the interface can be considered closed. In other
words, if you think you *might* want to extend an interface at run
time, you'd better say so at compile time somehow. I think that's
about as far as we can push it in the "final" direction.

[end digression]

: > I'm starting to think that any methods declared in roles are automatically


: > considered "multi" when composed, whether so declared or not.
:
: Hmm. So that would mean that we'd need a syntax for method replacement
: when we wanted it (which we'd need anyway if the method was our smallest
: unit of reuse rather than the role) and the only time we would get an
: error is when 2 (or more) roles included in a composition have exactly
: the same signature.

Right, I think.

: [slight digression, but methods are really singleton roles aren't they?]

In a sense, yes. Though just as with singleton classes, the type and
the thing it represents should not be confused.

: I'm not sure now whether I like idea of "shall" at all now. It seems


: better to just have every method of a role declare that it replaces the
: same-name method of the class. If the class doesn't want future roles
: replacing a given method, then we get an error. Oh, but without "shall",
: composition order would matter and there'd be no visual cue at
: composition time that a particular role is special. Drat.
:
: Surely we would need a way to do same-signature replacement on methods
: too? This would mildly argue against an implicit "multi".

I still think the right way to change policy is to write your own class
that *doesn't* define the methods you want the roles to override. The
name of a class has to represent *something*, if it doesn't represent
a fixed set of methods. I submit that it represents a consistent policy.
A different policy should have a different class name.

So I think that classes have to be in charge of role composition.
The actor chooses how to play the part, not vice versa. To the extent
that the actor can't choose, we're looking at the actor's traits,
not the actor's roles.

I try not to confuse roles and traits in my own life. Being the Perl
god is a role. Being a stubborn cuss is a trait. :-)

Larry

Chip Salzenberg

unread,
Dec 14, 2003, 11:17:25 PM12/14/03
to Language List
According to Larry Wall:

> If, by the time the entire program is parsed, nobody has said they
> want to extend an interface, then the interface can be considered
> closed.

What with C<eval STRING> and its various wrappers, when can the program
be said to be fully parsed? <- anticipating "Mu"
--
Chip Salzenberg - a.k.a. - <ch...@pobox.com>
"I wanted to play hopscotch with the impenetrable mystery of existence,
but he stepped in a wormhole and had to go in early." // MST3K

Jonathan Lang

unread,
Dec 15, 2003, 12:05:37 AM12/15/03
to Larry Wall, Language List
Larry Wall wrote:
> I think the class is still the final arbiter of what its objects
> are--there is no other entity that holds all the reins. If a class
> chooses to include a role, and that role violates the normal rules of
> roles, the class is still responsible for that (or else you need some
> babysitting code somewhere, hopefully in the metaclass). Maybe that's
> what a trait is--a renegade role.

Let's see if I've got this straight:

role methods supercede inherited methods;
class methods supercede role methods;
trait methods supercede class methods;
conflicting methods from multiple roles get discarded...
...but the class may alias or exclude any of the conflicting methods to
explicitly resolve the dispute.

Am I right so far? Maybe not; I noticed earlier that you've mentioned
that roles can be applied at compile-time using "does" or at run-time
using "but"; might _that_ be the defining feature as to whether the role
supercedes the class or vice versa? "does" supercedes inheritence, "has"
and "method" supercedes "does", "is" and "but" supercedes "has" and
"method"...

So how do you resolve conflicts between things that supercede the class?
First come first serve (as per slatherons)?

=====
Jonathan "Dataweaver" Lang

__________________________________
Do you Yahoo!?
New Yahoo! Photos - easier uploading and sharing.
http://photos.yahoo.com/

Larry Wall

unread,
Dec 15, 2003, 12:21:15 AM12/15/03
to Language List
On Sun, Dec 14, 2003 at 11:17:25PM -0500, Chip Salzenberg wrote:
: According to Larry Wall:

: > If, by the time the entire program is parsed, nobody has said they
: > want to extend an interface, then the interface can be considered
: > closed.
:
: What with C<eval STRING> and its various wrappers, when can the program
: be said to be fully parsed? <- anticipating "Mu"

Seems like the presence of C<eval> is a pretty clear evidence that you
want to extend something... :-)

Actually, I think making people declare what they want to extend
might actually provide a nice little safety mechanism for what can
be modified by the eval and what can't. It's not exactly Safe, but
it's a little safer.

Larry

Larry Wall

unread,
Dec 15, 2003, 12:57:20 AM12/15/03
to Language List
On Sun, Dec 14, 2003 at 09:05:37PM -0800, Jonathan Lang wrote:
: Larry Wall wrote:
: > I think the class is still the final arbiter of what its objects
: > are--there is no other entity that holds all the reins. If a class
: > chooses to include a role, and that role violates the normal rules of
: > roles, the class is still responsible for that (or else you need some
: > babysitting code somewhere, hopefully in the metaclass). Maybe that's
: > what a trait is--a renegade role.
:
: Let's see if I've got this straight:
:
: role methods supercede inherited methods;

But can defer via SUPER::

: class methods supercede role methods;

But can defer via ROLE:: or some such.

: trait methods supercede class methods;

I'm not sure traits work that way. I see them more as changing the
metaclass rules. They feel more like macros to me, where anything
goes, but you have to be a bit explicit and intentional.

: conflicting methods from multiple roles get discarded...

They aren't silently discarded--they throw a very public exception.
(But methods with differing "multi" signatures are not considered to
be conflicting, I hope.)

: ...but the class may alias or exclude any of the conflicting methods to
: explicitly resolve the dispute.

Right. Another possibility is that the class's method could be
declared to be the default multi method in case the type information
is not sufficient to decide which role's multi method should be called.
Maybe if it's declared "multi" it works that way. Otherwise it's just
called first automatically.

: Am I right so far? Maybe not; I noticed earlier that you've mentioned


: that roles can be applied at compile-time using "does" or at run-time
: using "but"; might _that_ be the defining feature as to whether the role
: supercedes the class or vice versa? "does" supercedes inheritence, "has"
: and "method" supercedes "does", "is" and "but" supercedes "has" and
: "method"...

No, I think I'm rejecting that notion as too complicated to keep
track of from moment to moment, and too much like slatherons in
policy wishy-washiness. The method precedence won't change from
compile time to run time.

: So how do you resolve conflicts between things that supercede the class?

: First come first serve (as per slatherons)?

Well, nothing much really supercedes the class. Even traits have
to be requested by the class, and if you have an entirely different
metaclass, it's probably declared with a different keyword than
C<class>. (But sure, multiple traits will have to applied in order
of declaration, and I don't doubt there will be ordering dependencies.)

I think the normative way to supercede a class should be to
subclass it. That's what OO is supposed to be all about, after all.
If we can keep that orthogonal to role composition, we stand a good
chance of being able to do a lot of what AOP claims to do without
the downsides of AOP's own slatheron approach. Or more precisely,
we can resort to AOP-style wrappers where we really need them, and
avoid them where we don't.

I'm probably spouting nonsense. I just hope it's good-sounding nonsense...

Larry

Chip Salzenberg

unread,
Dec 15, 2003, 8:37:36 AM12/15/03
to Language List
According to Larry Wall:

> Actually, I think making people declare what they want to extend
> might actually provide a nice little safety mechanism for what can
> be modified by the eval and what can't.

Well, I don't know about such 'safety' ... seems like a bone in a pond
to me ... but as a means to optimization it's a good idea, AFAICT.

Jonathan Scott Duff

unread,
Dec 15, 2003, 10:05:01 AM12/15/03
to Language List
On Sun, Dec 14, 2003 at 06:14:42PM -0800, Larry Wall wrote:
> On Sun, Dec 14, 2003 at 03:16:16AM -0600, Jonathan Scott Duff wrote:
> [ my ramblings about a mechanism for role methods to supercede class
> methods elided ]

>
> I think there's a simple way to solve this: If you're changing the
> policy of the class, then you're changing the class! Derive from the
> "defective" class and pull in the roles the way you prefer.

D'oh! You are absolutely correct.

Jonathan Lang

unread,
Dec 15, 2003, 10:02:53 PM12/15/03
to Larry Wall, Language List
Larry Wall wrote:

> Jonathan Lang wrote:
> : Let's see if I've got this straight:
> :
> : role methods supercede inherited methods;
>
> But can defer via SUPER::
>
> : class methods supercede role methods;
>
> But can defer via ROLE:: or some such.

Check, and check. Of course, SUPER:: works well in single inheritence,
but runs into problems of "which superclass?" in multi-inheritence; ROLE::
would on the surface appear to have that same problem, except that...

> : conflicting methods from multiple roles get discarded...
>
> They aren't silently discarded--they throw a very public exception.
> (But methods with differing "multi" signatures are not considered to
> be conflicting, I hope.)

(OK.)

> : ...but the class may alias or exclude any of the conflicting methods
> : to explicitly resolve the dispute.
>
> Right. Another possibility is that the class's method could be
> declared to be the default multi method in case the type information
> is not sufficient to decide which role's multi method should be called.
> Maybe if it's declared "multi" it works that way. Otherwise it's just
> called first automatically.

...meaning that the question of "which role do you mean?" has already been
addressed by the time the ROLE:: "deference" gets used.

Although I'm not following what you're saying here in terms of the third
means of disambiguation. Could someone provide an example, please?

> : trait methods supercede class methods;
>
> I'm not sure traits work that way. I see them more as changing the
> metaclass rules. They feel more like macros to me, where anything
> goes, but you have to be a bit explicit and intentional.

Well, the question is: the trait is providing a method with the same name
as a method provided by the class, and type information is insufficient to
distinguish between them; which one do I use? In the absence of
additional conflict resolution code, the possible options as I see them
would be:

1) the class supercedes the trait
2) the trait supercedes the class
3) an ambiguity exception gets thrown
4) the trait's method can't be called without explicitly naming the trait

Which of these three ought to hold true?

Second, where does the additional conflict resolution code go? In the
trait, in the class, or somewhere else?

> : Am I right so far? Maybe not; I noticed earlier that you've mentioned
> : that roles can be applied at compile-time using "does" or at run-time
> : using "but"; might _that_ be the defining feature as to whether the
> : role supercedes the class or vice versa? "does" supercedes
> : inheritence, "has" and "method" supercedes "does", "is" and "but"
> : supercedes "has" and "method"...
>
> No, I think I'm rejecting that notion as too complicated to keep
> track of from moment to moment, and too much like slatherons in
> policy wishy-washiness. The method precedence won't change from
> compile time to run time.

OK. My concern is that things like properties add new factors to the
ambiguity issue that you can't expect the class to know about, because
they're being introduced after the class was written. The fact that a
role supercedes inheritence makes sense to me (more precisely, it isn't
counterintuitive); that a class supercedes a role also makes sense to me,
as long as the role was there when the class was defined. But when you
add a role to the class after the fact, as in the case of properties, I
don't see how you can expect the class to be able to resolve the conflict.
What happens when the sticky note that you put on a microwave oven covers
up the display panel?

It's not so much run-time vs. compile-time as it is "while the class is
being written" and "after the class has been written", and the principle
that he who knows the most about what's going on should make the
decisions.

Perhaps this could be handled by requiring "sticky-note" roles (of which
properties are a subset) to be explicitly named when their methods are
called? That is, "after the fact" roles don't get flattened into the
class the way that normal roles do. That way, you're not requiring either
the class _or_ the role to resolve the conflict. This would be similar to
the relationship between positional parameters and named parameters, in
that the latter is there to let you add capabilities to an existing
function without disrupting the way that the function normally operates.
(OTOH, that's just about _all_ that it has in common.)

> : So how do you resolve conflicts between things that supercede the
> : class? First come first serve (as per slatherons)?
>
> Well, nothing much really supercedes the class. Even traits have
> to be requested by the class, and if you have an entirely different
> metaclass, it's probably declared with a different keyword than
> C<class>. (But sure, multiple traits will have to applied in order
> of declaration, and I don't doubt there will be ordering dependencies.)

My apologies; I'm apparently a bit weak on my object-oriented terminology.
I'm not quite sure what's being meant here by "metaclass", other than a
vague concept that it's somehow similar to the relationship between logic
and metalogic. Also, I was under the impression that the writers of the
"tTraits" paper that you referred us to disliked "mixins" largely because
they _did_ use an order-of-precedence conflict resolution scheme; surely
their concerns would apply equally well to what we're calling traits?

> I think the normative way to supercede a class should be to
> subclass it. That's what OO is supposed to be all about, after all.
> If we can keep that orthogonal to role composition, we stand a good
> chance of being able to do a lot of what AOP claims to do without
> the downsides of AOP's own slatheron approach. Or more precisely,
> we can resort to AOP-style wrappers where we really need them, and
> avoid them where we don't.

As I don't know what AOP is, this is largely lost on me. But I'm all for
keeping various aspects of perl orthogonal to each other if it's
reasonable to do so. Likewise, my main concern isn't so much "how to
supercede a class" as it is "how to keep a class from superceding a role
that it doesn't know about".

> I'm probably spouting nonsense. I just hope it's good-sounding
> nonsense...

More importantly, it seems to be _useful_ nonsense. I just hope that _my_
nonsense is more useful than it is annoying. :)

Michael Lazzaro

unread,
Dec 16, 2003, 1:48:23 AM12/16/03
to Language List

On Sunday, December 14, 2003, at 06:14 PM, Larry Wall wrote:
> But the agreement could be implied by silence. If, by the time the
> entire program is parsed, nobody has said they want to extend an
> interface, then the interface can be considered closed. In other
> words, if you think you *might* want to extend an interface at run
> time, you'd better say so at compile time somehow. I think that's
> about as far as we can push it in the "final" direction.

That seems a very fair rule, especially if it adds a smidge more speed.
Runtime extension will likely be very unusual -- requiring it to be
explicit seems reasonable.


> I'm probably spouting nonsense. I just hope it's good-sounding
> nonsense...

It's beyond good-sounding, it's frickin' awesome.

MikeL

Luke Palmer

unread,
Dec 16, 2003, 8:58:49 AM12/16/03
to Jonathan Lang, Larry Wall, Language List
Jonathan Lang writes:

> Larry Wall wrote:
> > Well, nothing much really supercedes the class. Even traits have
> > to be requested by the class, and if you have an entirely different
> > metaclass, it's probably declared with a different keyword than
> > C<class>. (But sure, multiple traits will have to applied in order
> > of declaration, and I don't doubt there will be ordering dependencies.)
>
> My apologies; I'm apparently a bit weak on my object-oriented terminology.
> I'm not quite sure what's being meant here by "metaclass", other than a
> vague concept that it's somehow similar to the relationship between logic
> and metalogic. Also, I was under the impression that the writers of the
> "tTraits" paper that you referred us to disliked "mixins" largely because
> they _did_ use an order-of-precedence conflict resolution scheme; surely
> their concerns would apply equally well to what we're calling traits?

I think metaclass is referring to the thing that knows how to associate
attributes with their corresponding objects, how do dispatch methods to
their corresponding code objects, and whatnot.

> > I think the normative way to supercede a class should be to
> > subclass it. That's what OO is supposed to be all about, after all.
> > If we can keep that orthogonal to role composition, we stand a good
> > chance of being able to do a lot of what AOP claims to do without
> > the downsides of AOP's own slatheron approach. Or more precisely,
> > we can resort to AOP-style wrappers where we really need them, and
> > avoid them where we don't.
>
> As I don't know what AOP is, this is largely lost on me. But I'm all for
> keeping various aspects of perl orthogonal to each other if it's
> reasonable to do so. Likewise, my main concern isn't so much "how to
> supercede a class" as it is "how to keep a class from superceding a role
> that it doesn't know about".

C<perldoc Aspect> does a pretty good job of introducing one to AOP, at
least the extent to which Perl is capable of it (which is quite a lot).

> > I'm probably spouting nonsense. I just hope it's good-sounding
> > nonsense...
>
> More importantly, it seems to be _useful_ nonsense. I just hope that _my_
> nonsense is more useful than it is annoying. :)

Luke

Luke Palmer

unread,
Dec 16, 2003, 9:05:19 AM12/16/03
to Michael Lazzaro, Language List
Michael Lazzaro writes:
>
> On Sunday, December 14, 2003, at 06:14 PM, Larry Wall wrote:
> >But the agreement could be implied by silence. If, by the time the
> >entire program is parsed, nobody has said they want to extend an
> >interface, then the interface can be considered closed. In other
> >words, if you think you *might* want to extend an interface at run
> >time, you'd better say so at compile time somehow. I think that's
> >about as far as we can push it in the "final" direction.
>
> That seems a very fair rule, especially if it adds a smidge more speed.
> Runtime extension will likely be very unusual

Unless you're me. Or Damian. Or a fair number of other programmers who
like to dive into the Perl Dark Side on a regular basis.

> -- requiring it to be explicit seems reasonable.

It seems so. Knowing Larry, I'm sure this is an ungrounded fear, but I
definitely want to be able to declare in a module "I'm going to be
screwing with stuff; keep out of my way," so that I don't impose any
awkward declarations on my module users. If that request can be made
more explicit in the cases where that's possible, great, but the general
declaration should be available.

Luke

Larry Wall

unread,
Dec 16, 2003, 12:07:08 PM12/16/03
to Language List
On Tue, Dec 16, 2003 at 07:05:19AM -0700, Luke Palmer wrote:
: Michael Lazzaro writes:
: >
: > On Sunday, December 14, 2003, at 06:14 PM, Larry Wall wrote:
: > >But the agreement could be implied by silence. If, by the time the
: > >entire program is parsed, nobody has said they want to extend an
: > >interface, then the interface can be considered closed. In other
: > >words, if you think you *might* want to extend an interface at run
: > >time, you'd better say so at compile time somehow. I think that's
: > >about as far as we can push it in the "final" direction.
: >
: > That seems a very fair rule, especially if it adds a smidge more speed.
: > Runtime extension will likely be very unusual
:
: Unless you're me. Or Damian. Or a fair number of other programmers who
: like to dive into the Perl Dark Side on a regular basis.
:
: > -- requiring it to be explicit seems reasonable.
:
: It seems so. Knowing Larry, I'm sure this is an ungrounded fear, but I
: definitely want to be able to declare in a module "I'm going to be
: screwing with stuff; keep out of my way," so that I don't impose any
: awkward declarations on my module users. If that request can be made
: more explicit in the cases where that's possible, great, but the general
: declaration should be available.

Okay, we'll call the general declaration:

use $&

or some such. :-)

Seriously, I hope we can provide a framework in which you can screw
around to your heart's content while modules are being compiled,
and to a lesser extent after compilation. But we'll never get to a
programming-in-the-large model if we can't limit most of the screwing
around to the lexical scope currently being compiled, or at least
to a known subset of the code. Modules that turn off optimization
for all other modules are going to be about as popular as $&. So
the general declaration should probably be something easy to see like:

use STRANGE_SEMANTICS_THAT_SLOW_EVERYONE_DOWN;

That will encourage people to be more specific about what they want
to pessimize. Certainly, your fancy module should be encouraged
to declare these things on behalf of its users if it can. I'm not
suggesting that Lukian or Damianly modules force such declarations onto
the users unless it's impossible for the module to know. And it seems
to me that with sufficient control over the user's grammar, you can
often get that information into your own fancy module somehow.
Might take a few macros though, or analysis of the user's code at
CHECK time (or maybe just before).

And in general, it's probably not necessary to declare all the new
interfaces, but only those interfaces known at compile time that want
to stay open. Any interfaces added at run time are probably assumed
to be open. So in some cases you might find yourself deriving a
single open class at compile time from which you can derive other
open classes later.

But still, the principle remains that original declarer of an
interface doesn't know in general whether its users are going to want
to extend it. At some point the users have to take responsibility
if they want their code to run fast. Or run at all...

So we need to make it very easy for users to provide this kind of
information when it's needed.

Larry

Michael Lazzaro

unread,
Dec 16, 2003, 3:06:46 PM12/16/03
to Language List

Exactly, assuming I correctly understand. :-)

My own first instinct would be that the run-time extensibility of a
particular interface/class would simply be a trait attached to that
class... by default, classes don't get it. By limiting or not limiting
the amount of runtime screwin' around you can do with the class, it is
therefore able to control the level of optimization that calls to
methods, etc., are given -- but specific to that particular
interface/class, not to the module and certainly not to the program in
general.

class Wombat is runtime_extensible { ... };

So everything is closed, except the specific classes which are not.
Even when you are (to use an example from my own code) making runtime
subclasses on-the-fly, you're almost always starting from some common
base class. (And 'almost' is probably an unneeded qualifier, there.
As is 'probably'.)

As far as users of your class being able to specify that they want
something runtime-extensible, when your original module didn't call for
it, I don't see that as a problem, if they can just add the trait to
your class shortly after they C<use> the package containing it, if such
things are possible -- or, for that matter, simply subclass your
original into a runtime_extensible class:

class Wombat { ... }; # Not runtime extensible
class MyWombat is Wombat
is runtime_extensible { ... }; # Runtime extensible


Now, it might be that declaring MyWombat to be runtime_extensible
actually silently disables some compile-time optimizations not only for
it, but for all its superclasses/roles/etc., depending on how
intelligent and far reaching those optimizations may be. Not sure.
Still, the fact that you are _requesting_ that happen is specific to
the particular class that needs it -- and should be associated with
that class, such that if that class later falls into disuse, the
optimizations silently reappear.

(At first glance, I am less sure of the need to have similar
functionality for entire modules, as opposed to classes, but perhaps
someone out there can come up with an example.)

MikeL

Chromatic

unread,
Dec 16, 2003, 3:20:05 PM12/16/03
to Michael Lazzaro, Language List
On Tue, 2003-12-16 at 12:06, Michael Lazzaro wrote:

> My own first instinct would be that the run-time extensibility of a
> particular interface/class would simply be a trait attached to that
> class... by default, classes don't get it.

That doesn't sound very dynamic.

At the post-OSCON design meetings, Larry suggested that the user of a
class or library could say "I'm not going to muck about with this at
runtime and any extra optimization would be nice, so go ahead and do
whatever you can do it."

Putting that opportunity on the user has several advantages:

- the library writer isn't responsible for getting the library
completely perfect, because library users can make changes if necessary
- the common case (run-time extension and manipulation) needs less code
(that is, you don't have to say "Mother, may I take advantage of the
features of the dynamic language I'm supposed to be?" to take advantage
of those features)
- the user of the library can choose specific optimizations when and
where he needs them

-- c

Gordon Henriksen

unread,
Dec 16, 2003, 3:20:31 PM12/16/03
to Language List
finally by default? None for me; thanks, though.

--

Gordon Henriksen
IT Manager
ICLUBcentral Inc.
gor...@iclub.com

Jonathan Scott Duff

unread,
Dec 16, 2003, 3:31:19 PM12/16/03
to Michael Lazzaro, Language List
On Tue, Dec 16, 2003 at 12:06:46PM -0800, Michael Lazzaro wrote:
> As far as users of your class being able to specify that they want
> something runtime-extensible, when your original module didn't call for
> it, I don't see that as a problem, if they can just add the trait to
> your class shortly after they C<use> the package containing it, if such
> things are possible

I think it kind of hinges on the ability to undo optimizations.

Just to restate things a bit to make sure I understand ... when perl
compiles a class, it assumes it's closed and accordingly applies
whatever optimizations it can unless it sees that the programmer
explicitly asked otherwise. Those classes that are "closed" can be
opened at run-time and the user pays the penalty then when they try to
modify the class (and pays twice because of the compile-time
optimizations that perl applied and are now undoing).

But does everybody pay some penalty because of it? I hope not.
Presumably we keep the source around for a reparse if necessary
anyway? Or perhaps we have "unoptimized" bytecode laying around ready
to be switched in for the optimized bytecode when necessary.

> class Wombat { ... }; # Not runtime extensible
> class MyWombat is Wombat
> is runtime_extensible { ... }; # Runtime extensible
>
> Now, it might be that declaring MyWombat to be runtime_extensible
> actually silently disables some compile-time optimizations not only for
> it, but for all its superclasses/roles/etc., depending on how
> intelligent and far reaching those optimizations may be.

I hope not.

> Not sure.
> Still, the fact that you are _requesting_ that happen is specific to
> the particular class that needs it -- and should be associated with
> that class,

Yep.

> such that if that class later falls into disuse, the
> optimizations silently reappear.

That would be *some* trick!

Chip Salzenberg

unread,
Dec 16, 2003, 5:14:11 PM12/16/03
to Michael Lazzaro, Language List
According to Jonathan Scott Duff:

> Those classes that are "closed" can be opened at run-time and the
> user pays the penalty then when they try to modify the class [...]

The optimization that can be reversed is not the true optimization.

Michael Lazzaro

unread,
Dec 16, 2003, 6:06:37 PM12/16/03
to Language List

On Tuesday, December 16, 2003, at 12:20 PM, Gordon Henriksen wrote:
> finally by default? None for me; thanks, though.

I don't think so; we're just talking about whether you can extend a
class at _runtime_, not _compiletime_. Whether or not Perl can have
some degree of confidence that, once a program is compiled, it won't
have to assume the worst-case possibility of runtime alteration of
every class, upon every single method call, just in case you've screwed
with something.

They still aren't "final" classes, in that you can subclass them at
will. You just can't subclass them _runtime_, via C<eval>, unless
you've specifically marked that you want to allow that for that
_specific_ class.

As Larry hypothesized:


> The other reason for "final" is to make it easy for the compiler
> to optimize. That's also problematical. As implemented by Java,
> it's a premature optimization. The point at which you'd like to
> know this sort of thing is just after parsing the entire program and
> just before code generation. And the promises have to come from
> the users of interfaces, not the providers, because the providers
> don't know how their services are going to be used. Methods, roles,
> and classes may never declare themselves final. They may be declared
> final only by the agreement of all their users.
>

> But the agreement could be implied by silence. If, by the time the
> entire program is parsed, nobody has said they want to extend an
> interface, then the interface can be considered closed. In other
> words, if you think you *might* want to extend an interface at run
> time, you'd better say so at compile time somehow. I think that's
> about as far as we can push it in the "final" direction.

-and-

> Actually, I think making people declare what they want to extend
> might actually provide a nice little safety mechanism for what can

> be modified by the eval and what can't. It's not exactly Safe, but
> it's a little safer.

-and-

> Seriously, I hope we can provide a framework in which you can screw
> around to your heart's content while modules are being compiled,
> and to a lesser extent after compilation. But we'll never get to a
> programming-in-the-large model if we can't limit most of the screwing
> around to the lexical scope currently being compiled, or at least
> to a known subset of the code.


So, if I may interpret that; it might not be so bad to have to declare
whether or not you were going to extend/alter a class at runtime, in
order that Perl could optimize what it knows at compile-time for the
99.5% of the classes that you wouldn't be doing that for.

MikeL

Luke Palmer

unread,
Dec 16, 2003, 5:48:36 PM12/16/03
to Chip Salzenberg, Michael Lazzaro, Language List
Chip Salzenberg writes:
> According to Jonathan Scott Duff:
> > Those classes that are "closed" can be opened at run-time and the
> > user pays the penalty then when they try to modify the class [...]
>
> The optimization that can be reversed is not the true optimization.

While poetic and concise, I think that statement needs to be driven into
the ground a bit more.

Over on p6i, I think we're basically in agreement that the ability to
undo optimizations is nothing we can count on. Unless there is a
breakthrough in computer science any time soon, this while loop:

sub one() { 1 };
sub go() {
my $x = 0;
while $x++ < one { # loop optimized away
%main::{'&one'} = sub { 10 };
print "Boing!\n";
}
}

Is not something that can can be re-inserted when we find out one() has
changed. While it's possible to make it so go() is unoptimized on the
next call, that's not good enough. We expect changes to act instantly.

But if you separate parsing and code-generation time, you can make
optimizations earlier based on declarations later, which is just fine.
It allows you to say:

use PresumptuousModule << SomeClass >>;
class SomeClass is extensible { };

Then even if the writer of PresumptuousModule thinks you'll be better
off with the optimization, you can tell him otherwise. But you have to
do it before the code is generated.

Luke

Michael Lazzaro

unread,
Dec 16, 2003, 6:31:57 PM12/16/03
to Language List

On Tuesday, December 16, 2003, at 03:00 PM, Luke Palmer wrote:
> But Perl hinges on laziness, doesn't it? Eh, I trust that Perl 6 will
> make it easy to figure that out in most cases. I was coming from the
> perspective that 90% of my projects don't need speed; but I can say no
> such thing on account of my users. And what about that
> un-accounted-for
> 10%?

As someone who has 90% of their projects relying very critically on
speed, and who has had to battle a number of clients' IT departments
over the years in defense of said speed compared to other popular
languages which, out of spite, I will not name, I beg you to never
speak or think that sentence again.

;-)

MikeL

Luke Palmer

unread,
Dec 16, 2003, 6:00:01 PM12/16/03
to Language List

Hmm, I guess that's true. A module author shouldn't have the the
freedom to say that his classes are completely untouchable, because he
doesn't know what you're going to be doing with them. But
correspondingly, I guess, a module author shouldn't have the freedom to
slow everybody down because he was lazy about figuring out what needed
to be declared "open".

But Perl hinges on laziness, doesn't it? Eh, I trust that Perl 6 will
make it easy to figure that out in most cases. I was coming from the
perspective that 90% of my projects don't need speed; but I can say no
such thing on account of my users. And what about that un-accounted-for
10%?

Perhaps the real detterent to using such a thing would be making it
generate a warning when -w is on. You get the peer pressure thing;
people frown upon you when you use the pragma unwisely.

So, yeah, I agree with you now.

Luke

Chip Salzenberg

unread,
Dec 16, 2003, 7:01:14 PM12/16/03
to Michael Lazzaro, Language List
According to Michael Lazzaro:

> As someone who has 90% of their projects relying very critically on
> speed

... an anecdote ...

> and who has had to battle a number of clients' IT departments
> over the years in defense of said speed compared to other popular
> languages which, out of spite, I will not name,

... and a public relations issue.

Let us not confuse them.

Piers Cawley

unread,
Dec 16, 2003, 7:11:59 PM12/16/03
to Language List
Larry Wall <la...@wall.org> writes:

Or the debugger. Or a refactoring tool. Or a Class browser...

> So the general declaration should probably be something easy to see
> like:
>
> use STRANGE_SEMANTICS_THAT_SLOW_EVERYONE_DOWN;

No question about that.

> That will encourage people to be more specific about what they want
> to pessimize. Certainly, your fancy module should be encouraged
> to declare these things on behalf of its users if it can. I'm not
> suggesting that Lukian or Damianly modules force such declarations onto
> the users unless it's impossible for the module to know. And it seems
> to me that with sufficient control over the user's grammar, you can
> often get that information into your own fancy module somehow.
> Might take a few macros though, or analysis of the user's code at
> CHECK time (or maybe just before).

When you say CHECK time, do you mean there'll be a CHECK phase for
code that gets required at run time?

Piers Cawley

unread,
Dec 16, 2003, 7:15:04 PM12/16/03
to Michael Lazzaro, Language List
Michael Lazzaro <mlaz...@cognitivity.com> writes:

> On Tuesday, December 16, 2003, at 12:20 PM, Gordon Henriksen wrote:
>> finally by default? None for me; thanks, though.
>
> I don't think so; we're just talking about whether you can extend a
> class at _runtime_, not _compiletime_. Whether or not Perl can have
> some degree of confidence that, once a program is compiled, it won't
> have to assume the worst-case possibility of runtime alteration of
> every class, upon every single method call, just in case you've
> screwed with something.

There's still a hell of a lot of stuff you can do with 'cached'
optimization that can be thrown away if anything changes. What the
'final' type declarations would do is allow the compiler to throw away
the unoptimized paths and the checks for dynamic changes that mean the
optimization has to be thrown out and started again.

Michael Lazzaro

unread,
Dec 16, 2003, 8:08:31 PM12/16/03
to Language List

On Tuesday, December 16, 2003, at 04:01 PM, Chip Salzenberg wrote:

> According to Michael Lazzaro:
>> As someone who has 90% of their projects relying very critically on
>> speed
>
> ... an anecdote ...

Yes.

>> and who has had to battle a number of clients' IT departments
>> over the years in defense of said speed compared to other popular
>> languages which, out of spite, I will not name,
>
> ... and a public relations issue.

Yes, again.

> Let us not confuse them.

I'm not sure I understand which part of that is in conflict. Is it the
premise that some people use Perl in environments in which speed is an
issue, the premise that Perl5 has a public relations issue about being
inappropriate for speed-critical environments, or the conflation that
someone that works in speed-critical environments, and wishes to use
Perl, is going to run up against the public-relations issue?

MikeL

John Macdonald

unread,
Dec 16, 2003, 9:37:11 PM12/16/03
to Piers Cawley, Michael Lazzaro, Language List
On Wed, Dec 17, 2003 at 12:15:04AM +0000, Piers Cawley wrote:
> There's still a hell of a lot of stuff you can do with 'cached'
> optimization that can be thrown away if anything changes. What the
> 'final' type declarations would do is allow the compiler to throw away
> the unoptimized paths and the checks for dynamic changes that mean the
> optimization has to be thrown out and started again.

As Luke pointed out in an earlier message,
you can encounter grave difficulty (i.e. halting
problem unsolvable sort of difficulty) in trying to
unoptimize a piece of code that is in the middle of
being executed. Just about any subroutine call might
(but almost always won't :-) happen to execute code
that makes the current subroutine have to revert
to unoptimized (or differently optimized) form.
When that subroutine call returns after such a rare
occurrence, it can't return to the unoptimized code
(because there could be missing context because the
calling routine got this far using the optimized code
and may have skipped stuff that is (now) necessary)
and it can't return to the old code (because its
optimization might now be wrong).

Chip Salzenberg

unread,
Dec 16, 2003, 8:36:03 PM12/16/03
to Michael Lazzaro, Language List
According to Michael Lazzaro:

> On Tuesday, December 16, 2003, at 04:01 PM, Chip Salzenberg wrote:
> >... an anecdote ...

> >... and a public relations issue.
> >Let us not confuse them.
>
> I'm not sure I understand which part of that is in conflict.

Speed is for users. PR is for non-users.

You want speed? OK, we can talk about the actual speed you actually
need based on your actual usage patterns. But from a design
perspective you're a collection of anecote, not a user base; so your
usage patterns may be irrelevant to Perl in the big picture.

In a separate matter, non-users may perceive Perl {5,6} to be too slow
for their needs; more to the point, they may *assume* that it is too
slow without research and testing. That assumption is a public
relations issue -- ironically, one which is fundamentally disconnected
from the question of Perl's _actual_ efficiency.

Larry Wall

unread,
Dec 16, 2003, 9:10:40 PM12/16/03
to Language List
On Wed, Dec 17, 2003 at 12:11:59AM +0000, Piers Cawley wrote:
: When you say CHECK time, do you mean there'll be a CHECK phase for

: code that gets required at run time?

Dunno about that. When I say CHECK time I'm primarily referring
to the end of the main compilation. Perl 5 appears to ignore CHECK
blocks declared at run time, so in the absence of other considerations
I suspect Perl 6 might do the same.

Larry

Michael Lazzaro

unread,
Dec 16, 2003, 9:55:46 PM12/16/03
to Language List

On Tuesday, December 16, 2003, at 05:36 PM, Chip Salzenberg wrote:
> Speed is for users. PR is for non-users.
>
> You want speed? OK, we can talk about the actual speed you actually
> need based on your actual usage patterns. But from a design
> perspective you're a collection of anecote, not a user base; so your
> usage patterns may be irrelevant to Perl in the big picture.
>
> In a separate matter, non-users may perceive Perl {5,6} to be too slow
> for their needs; more to the point, they may *assume* that it is too
> slow without research and testing. That assumption is a public
> relations issue -- ironically, one which is fundamentally disconnected
> from the question of Perl's _actual_ efficiency.


Well, just for clarification; in my anecdotal case (server-side web
applications), the speed I actually need is "as much as I can get", and
"all the time". Every N cycles I save represents an increase in peak
traffic capabilities per server, which is, from a marketing
perspective, essential.

If a potential client company needs to decide between two server-based
products -- my Perl based product, and a competing Java-based one --
one of the first questions they ask is "how much traffic can it handle
for X dollars of hardware and software". I don't have to win that
benchmark, but I have to be close. Otherwise I don't get to play.

I agree, it is frequently the case that the question of speed is made
critical by people who most assuredly do not need it. But they still
decide that way, and I have found that asserting to them that speed is
not important has been... well, less than effective. I do not doubt
that P6 will be much more competitive, speed-wise, than P5 -- but if it
could actually _win_ a few benchmarks, it would turn my company's use
of Perl from a PR problem to a PR advantage.


> your usage patterns may be irrelevant to Perl in the big picture.

The thought has crossed my mind repeatedly, believe me.

MikeL

Luke Palmer

unread,
Dec 16, 2003, 10:43:08 PM12/16/03
to Michael Lazzaro, Language List
Michael Lazzaro writes:
> I agree, it is frequently the case that the question of speed is made
> critical by people who most assuredly do not need it. But they still
> decide that way, and I have found that asserting to them that speed is
> not important has been... well, less than effective. I do not doubt
> that P6 will be much more competitive, speed-wise, than P5 -- but if it
> could actually _win_ a few benchmarks, it would turn my company's use
> of Perl from a PR problem to a PR advantage.

In the presence of parrot's JIT, competing should be no problem. I'm
not entirely sure Perl 6 will be faster than Perl 5 on the average. But
the difference is that Perl 6 will allow you to make fast code where you
need it. For instance (and the main one, probably), using native
(lowercase) types allows you to JIT, and using JIT is just... well, you
have to see it for yourself. Amazing. But since, as I've said, I don't
do speed-critical work, I won't be usually using lowercase types. And
that trades me flexibility for speed.

And from what I've seen of Java, if you need speed, hand-optimizing your
inner loop to parrot assembly should blow Java out of the water.
Without needing a C compiler (I despise XS).

Luke

Rafael Garcia-Suarez

unread,
Dec 17, 2003, 1:20:22 AM12/17/03
to perl6-l...@perl.org
Larry Wall wrote in perl.perl6.language :

This has proven to be inconvenient except for a few specialized usages,
such as the B::/O compiler framework.

There's a need (more or less) for special blocks that can be run at the
end of the compilation phase of any arbitrary compilation unit.

Piers Cawley

unread,
Dec 17, 2003, 4:10:18 AM12/17/03
to Language List
Larry Wall <la...@wall.org> writes:

I feared that might be the case.

Simon Cozens

unread,
Dec 17, 2003, 4:39:05 AM12/17/03
to perl6-l...@perl.org
mlaz...@cognitivity.com (Michael Lazzaro) writes:
> Well, just for clarification; in my anecdotal case (server-side web
> applications), the speed I actually need is "as much as I can get",
> and "all the time". Every N cycles I save represents an increase in
> peak traffic capabilities per server, which is, from a marketing
> perspective, essential.

The desire to optimize the hell out of Perl 6 is a good one, but surely
you optimize when there is a problem, not when before. Is there a problem
with the speed you're getting from Perl 6 at the moment?

--
<evilPetey> I often think I'd get better throughput yelling at the modem.

Gordon Henriksen

unread,
Dec 16, 2003, 6:55:56 PM12/16/03
to Michael Lazzaro, Language List
Michael Lazzaro wrote:

> I don't think so; we're just talking about whether you can extend a
> class at _runtime_, not _compiletime_. Whether or not Perl can have
> some degree of confidence that, once a program is compiled, it won't
> have to assume the worst-case possibility of runtime alteration of
> every class, upon every single method call, just in case
> you've screwed with something.

That's a cute way of glossing over the problem.

How do you truly know when runtime is in the first place? Imagine an
application server which parses and loads code from files on-demand.
This shouldn't be difficult. Imagine that that code references a
system of modules.

Imagine if Perl "finalizes" classes after "primary compilation"
(after parsing, say, an ApacheHandler file), and proceeds to behave
quite differently indeed afterwards.

Imagine that a perfectly innocent coder finds that his class
library doesn't run the same (doesn't run at all) under the
application server as it does when driven from command line scripts:
His method overrides don't take effect (or, worse, Perl tells him he
can't even compile them because the class is already "finalized"! And
he thought Perl was a dynamic language!).

What's his recourse? Nothing friendly. Tell Perl that he's going
to subclass the classes he subclasses? Why? He already subclasses
them! Isn't that "tell" enough? And how? Some obscure configuration
file of the application server, no doubt. And now the app server needs
to be restarted if that list changes. His uptime just went down. And
now he can't have confidence that his system will continue to behave
consistently over time; "apachectl restart" becomes a part of his
development troubleshooting lexicon.

Java doesn't make him do that; HotSpot can make this optimization at
runtime and back it out if necessary. Maybe he'll just write a JSP
instead.

C# and VB.NET do likewise. ASP.NET isn't looking so bad, either. The
.NET Frameworks are sure a lot less annoying than the Java class
library, after all.


Point of fact, for a large set of important usage cases, Perl simply
can't presume that classes will EVER cease being introduced into the
program. That means it can NEVER make these sorts of optimizations
unless it is prepared to back them out. Even in conventional programs,
dynamic class loading is increasingly unavoidable. Forcing virtuous
programmers to declare "virtual" (lest their program misbehave or
their perfectly valid bytecode fail to load, or their perfectly valid
source code fail to compile) is far worse than allowing foolish
programmers to declare "final."

Making semantic distinctions of this scale between "compile time"
and "runtime" will be a significant blow to Perl, which has always been
strengthened by its dynamism. Its competitors do not include such
artifacts; they perform class finalization optimizations on the fly,
and, despite the complexity of the task, are prepared to back out these
optimizations at runtime--while the optimized routines are executing,
if necessary. Yes, this requires synchronization points, notifications
(or inline checks), and limits code motion. Better than the
alternative, I say. It is very simply a huge step backwards to
create a semantic wall between primary compilation and program
execution.

So write the complicated code to make it work right.
- or -
Take the performance hit and go home.

Dynamism has a price. Perl has always paid it in the past. What's
changed?

Larry Wall

unread,
Dec 17, 2003, 2:24:42 PM12/17/03
to perl6-l...@perl.org
On Wed, Dec 17, 2003 at 06:20:22AM -0000, Rafael Garcia-Suarez wrote:
: Larry Wall wrote in perl.perl6.language :

Well, that's what I'd call an "other consideration". :-)

Larry

Larry Wall

unread,
Dec 17, 2003, 2:54:34 PM12/17/03
to Language List

Any such application server would probably just

use DYNAMIC_EVERYTHING;

(or whatever we call it) and have done with it.

: Java doesn't make him do that; HotSpot can make this optimization at


: runtime and back it out if necessary. Maybe he'll just write a JSP
: instead.

If Parrot turns out to be able to make this optimization, then the
individual declarations of dynamism merely become hints that it's
not worth trying to optimize a particular class because it'll get
overridden anyway. It's still useful information on an individual
class basis. The only thing that is bogus in that case is the global
DYNAMIC_EVERYTHING declaration in the application server. So I could
be argued into making that the default. A program that wants a static
analysis at CHECK time for speed would then need to declare that.
The downside of making that the default is that then people won't
declare which classes need to remain extensible under such a regime.
That's another reason such a declaration does not belong with the
class itself, but with the users of the class. If necessary, the main
program can pick out all the classes it things need to remain dymanic:

module Main;
use STATIC_CLASS_CHECK;
use class Foo is dynamic;
use class Bar is dynamic;

or whatever the new C<use> syntax will be in A11...

: C# and VB.NET do likewise. ASP.NET isn't looking so bad, either. The


: .NET Frameworks are sure a lot less annoying than the Java class
: library, after all.

On the other hand, those guys are also doing a lot more mandatory
static typing to get their speed, and that's also annoying.
(Admittedly, they're working on supporting dynamic languages better.)

: Point of fact, for a large set of important usage cases, Perl simply


: can't presume that classes will EVER cease being introduced into the
: program. That means it can NEVER make these sorts of optimizations
: unless it is prepared to back them out. Even in conventional programs,
: dynamic class loading is increasingly unavoidable. Forcing virtuous
: programmers to declare "virtual" (lest their program misbehave or
: their perfectly valid bytecode fail to load, or their perfectly valid
: source code fail to compile) is far worse than allowing foolish
: programmers to declare "final."

The relative merit depends on who declares the "final", methinks. But
if we can avoid both problems, I think we should.

: Making semantic distinctions of this scale between "compile time"


: and "runtime" will be a significant blow to Perl, which has always been
: strengthened by its dynamism. Its competitors do not include such
: artifacts; they perform class finalization optimizations on the fly,
: and, despite the complexity of the task, are prepared to back out these
: optimizations at runtime--while the optimized routines are executing,
: if necessary. Yes, this requires synchronization points, notifications
: (or inline checks), and limits code motion. Better than the
: alternative, I say. It is very simply a huge step backwards to
: create a semantic wall between primary compilation and program
: execution.
:
: So write the complicated code to make it work right.
: - or -
: Take the performance hit and go home.
:
: Dynamism has a price. Perl has always paid it in the past. What's
: changed?

Nothing, except I'd like like to at least have the *option* of not
paying the price. But you've argued me into keeping the default
the same, because it works better if and when Parrot *does* support
deoptimization/reoptimization, and even before then, because the
optional dynamic hints can be used to work around spots where the
developing optimizer is broken. And in the limiting case where
the optimizer is completely broken because it's not implemented
yet, we get to work around that too. Optionally...

Larry

David Wheeler

unread,
Dec 18, 2003, 2:09:11 AM12/18/03
to Rafael Garcia-Suarez, perl6-l...@perl.org
On Dec 16, 2003, at 10:20 PM, Rafael Garcia-Suarez wrote:

> There's a need (more or less) for special blocks that can be run at the
> end of the compilation phase of any arbitrary compilation unit.

This would be especially useful in an environment such as mod_perl,
where CHECK and INIT blocks currently _never_ execute, no matter when
they're declared.

Regards,

David

--
David Wheeler AIM: dwTheory
da...@kineticode.com ICQ: 15726394
http://www.kineticode.com/ Yahoo!: dew7e
Jabber: The...@jabber.org
Kineticode. Setting knowledge in motion.[sm]

David Wheeler

unread,
Dec 18, 2003, 2:10:22 AM12/18/03
to Simon Cozens, perl6-l...@perl.org
On Dec 17, 2003, at 1:39 AM, Simon Cozens wrote:

> The desire to optimize the hell out of Perl 6 is a good one, but surely
> you optimize when there is a problem, not when before. Is there a
> problem
> with the speed you're getting from Perl 6 at the moment?

Yes, it's taking too long to be released! ;-)

Regards,

David (Who wants to start writing Perl 6 applications yesterday.)

Larry Wall

unread,
Dec 18, 2003, 12:49:05 PM12/18/03
to perl6-l...@perl.org
On Wed, Dec 17, 2003 at 06:20:22AM -0000, Rafael Garcia-Suarez wrote:
: Larry Wall wrote in perl.perl6.language :

Well, if you want to run at the end of the current compilation unit, a
BEGIN block at the end is close to what you want. Admittedly, the BEGIN
block can't easily *know* that it's the last thing...

That's not to say we can't improve the semantics of CHECK and INIT.

Larry

Larry Wall

unread,
Dec 19, 2003, 2:40:59 PM12/19/03