Google Gruplar, artık yeni Usenet gönderilerini veya aboneliklerini desteklememektedir. Geçmişteki içerikler görüntülenebilir kalmaya devam edecek.

Roles and Mix-ins?

9 görüntüleme
İlk okunmamış mesaja atla

Chris Shawmail

okunmadı,
12 Ara 2003 19:19:1012.12.2003
alıcı perl6-l...@perl.org
I'm still digesting the vocabulary thread, but while I do, let me ask a
question that's probably crystal clear to everyone else.

Do roles act as a form of mix-in, as Ruby modules may, and Objective-C
protocols do?

Would the following two snippets be at all equivalent?

# Perl6
role Talk {
method say_hello {
print "Hello world!\n"
}
}

class Foo does Talk { ... }

Foo.new.say_hello;

# Ruby
module Talk
def say_hello
puts "Hello world!"
end
end

class Foo
include Talk
end

Foo.new.say_hello

---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.545 / Virus Database: 339 - Release Date: 11/27/2003


Luke Palmer

okunmadı,
13 Ara 2003 06:57:1713.12.2003
alıcı Chris Shawmail (E-mail), perl6-l...@perl.org
Chris Shawmail (E-mail) writes:
> I'm still digesting the vocabulary thread, but while I do, let me ask a
> question that's probably crystal clear to everyone else.
>
> Do roles act as a form of mix-in, as Ruby modules may, and Objective-C
> protocols do?
>
> Would the following two snippets be at all equivalent?

Probably, depending on what's in the eventual definition of Foo.

Roles are quite similar to mixins (as the Traits paper said that they
were inspired by mixins), but they're distinctly not the same.

For one, one role's methods don't silently override another's. Instead,
you get, er, role conflict and you have to disambiguate yourself. For
two, you can attach new roles to an object at runtime (I don't know if
you can do this with mixins, actually).

> # Perl6
> role Talk {
> method say_hello {
> print "Hello world!\n"
> }
> }
>
> class Foo does Talk { ... }
>
> Foo.new.say_hello;
>
> # Ruby
> module Talk
> def say_hello
> puts "Hello world!"
> end
> end
>
> class Foo
> include Talk
> end
>
> Foo.new.say_hello

Luke

Larry Wall

okunmadı,
13 Ara 2003 14:12:3113.12.2003
alıcı perl6-l...@perl.org
On Sat, Dec 13, 2003 at 04:57:17AM -0700, Luke Palmer wrote:
: Chris Shawmail (E-mail) writes:
: > I'm still digesting the vocabulary thread, but while I do, let me ask a
: > question that's probably crystal clear to everyone else.
: >
: > Do roles act as a form of mix-in, as Ruby modules may, and Objective-C
: > protocols do?
: >
: > Would the following two snippets be at all equivalent?
:
: Probably, depending on what's in the eventual definition of Foo.
:
: Roles are quite similar to mixins (as the Traits paper said that they
: were inspired by mixins), but they're distinctly not the same.
:
: For one, one role's methods don't silently override another's. Instead,
: you get, er, role conflict and you have to disambiguate yourself. For
: two, you can attach new roles to an object at runtime (I don't know if
: you can do this with mixins, actually).

Yes, you can. The mixin creates a new singleton class every time
you do it, derived from the previous class. My current thinking is
that run-time roles work a little differently. You get a singleton
class for the object the first time you apply a property, so that each
object's properties remain distinct. However, subsequent properties
re-use the existing singleton class, and do the same role-conflict
checks at run time that "does" would do in the class definition
at compile time. Furthermore, the singleton class is not really
derived from the original class, but just presents a different view
of the same class, so that, from the viewpoint of the object, every
role has the same standing, and run-time roles aren't privileged
above compile-time roles, as they would be if the singleton class
were really derived from the original class. In a sense, the object
thinks it's recomposing the original class, but it's slightly wrong.

If you really want new roles to override old roles, it's easy enough
to throw a real derivation in there. But the Traits paper argues,
and I'm inclined to agree with them, that this not what you want for
the default behavior. Now the Traits paper was really only worrying
about composing classes at compile time, and we're extending it to
run-time. That means you can get name collisions at run time when
you add a role. I still think it's better to catch that sort of goof
earlier than later. That means the syntax for C<but> may evolve to
look more like the syntax for C<does>. Whatever that looks like...

Larry

David Storrs

okunmadı,
5 Oca 2004 11:46:085.01.2004
alıcı perl6-l...@perl.org

On Sat, Dec 13, 2003 at 11:12:31AM -0800, Larry Wall wrote:
> On Sat, Dec 13, 2003 at 04:57:17AM -0700, Luke Palmer wrote:

> : For one, one role's methods don't silently override another's. Instead,
> : you get, er, role conflict and you have to disambiguate yourself.

How do you disambiguate?

> : For


> : two, you can attach new roles to an object at runtime (I don't know if
> : you can do this with mixins, actually).
>
> Yes, you can. The mixin creates a new singleton class every time
> you do it, derived from the previous class. My current thinking is
> that run-time roles work a little differently. You get a singleton
> class for the object the first time you apply a property, so that each
> object's properties remain distinct. However, subsequent properties
> re-use the existing singleton class, and do the same role-conflict
> checks at run time that "does" would do in the class definition
> at compile time. Furthermore, the singleton class is not really
> derived from the original class, but just presents a different view
> of the same class, so that, from the viewpoint of the object, every
> role has the same standing, and run-time roles aren't privileged
> above compile-time roles, as they would be if the singleton class
> were really derived from the original class. In a sense, the object
> thinks it's recomposing the original class, but it's slightly wrong.

After reading this several times, I _think_ I understand. Let me
check: imagine that the original class is a text buffer going from
0-99. We have two roles (A and B), each of length 100. Objects of
various types can then see different segments of the buffer (i.e.,
different methods/properties/whatever), as follows:

Type Can see
---- -------
Class 1-100
A 101-199
B 200-299
Class+A 1-100,101-199
Class+B 1-100,200-299
Class+A+B 1-100,101-199,200-299

Is that right?

Dave

Jonathan Lang

okunmadı,
5 Oca 2004 16:06:085.01.2004
alıcı perl6-l...@perl.org
David Storrs wrote:
> On Sat, Dec 13, 2003 at 11:12:31AM -0800, Larry Wall wrote:
> > On Sat, Dec 13, 2003 at 04:57:17AM -0700, Luke Palmer wrote:
> > : For one, one role's methods don't silently override another's.
> > : Instead, you get, er, role conflict and you have to disambiguate
> > : yourself.
>
> How do you disambiguate?

For most cases, you either alias or exclude one or the other of the
offending methods from the final class, rendering the ambiguity moot.
However, role conflict not disambiguiated in either of the above manners
throws an exception (presumably with a list of the ambiguous methods),
letting you write code to disambiguate in whatever manner you like on a
case-by-case basis. The only question I have about such an exception is
what sort of information your ambiguity handler code receives.


=====
Jonathan "Dataweaver" Lang

__________________________________
Do you Yahoo!?
Find out what made the Top Yahoo! Searches of 2003
http://search.yahoo.com/top2003

Luke Palmer

okunmadı,
6 Oca 2004 04:51:276.01.2004
alıcı perl6-l...@perl.org
David Storrs writes:
>
> On Sat, Dec 13, 2003 at 11:12:31AM -0800, Larry Wall wrote:
> > On Sat, Dec 13, 2003 at 04:57:17AM -0700, Luke Palmer wrote:
>
> > : For one, one role's methods don't silently override another's. Instead,
> > : you get, er, role conflict and you have to disambiguate yourself.
>
> How do you disambiguate?

Let's see...

role Dog {
method bark() { print "Ruff!" }
}
role Tree {
method bark() { print "Rough!" }
}
class Trog
does Dog does Tree {
method bark() { .Dog::bark() }
}
}

Perhaps something like that. In any case, you do it by putting the
offending method directly in the aggregating class.

> After reading this several times, I _think_ I understand. Let me
> check: imagine that the original class is a text buffer going from
> 0-99. We have two roles (A and B), each of length 100. Objects of
> various types can then see different segments of the buffer (i.e.,
> different methods/properties/whatever), as follows:
>
> Type Can see
> ---- -------
> Class 1-100
> A 101-199
> B 200-299
> Class+A 1-100,101-199
> Class+B 1-100,200-299
> Class+A+B 1-100,101-199,200-299
>
> Is that right?

Umm... I'm not sure that's what I'd use roles for. And I'm not sure how
roles with associated data are supposed to work yet. But I think you
have the general idea.

Luke

Joe Gottman

okunmadı,
6 Oca 2004 20:33:376.01.2004
alıcı Perl6

----- Original Message -----
From: "Luke Palmer" <fibo...@babylonia.flatirons.org>
To: <perl6-l...@perl.org>
Sent: Tuesday, January 06, 2004 4:51 AM
Subject: [perl] Re: Roles and Mix-ins?


> David Storrs writes:
> >
> > On Sat, Dec 13, 2003 at 11:12:31AM -0800, Larry Wall wrote:
> > > On Sat, Dec 13, 2003 at 04:57:17AM -0700, Luke Palmer wrote:
> >
> > > : For one, one role's methods don't silently override another's.
Instead,
> > > : you get, er, role conflict and you have to disambiguate yourself.
> >
> > How do you disambiguate?
>
> Let's see...
>
> role Dog {
> method bark() { print "Ruff!" }
> }
> role Tree {
> method bark() { print "Rough!" }
> }
> class Trog
> does Dog does Tree {
> method bark() { .Dog::bark() }
> }
> }
>
> Perhaps something like that. In any case, you do it by putting the
> offending method directly in the aggregating class.
>

How about something like
class Trog
does Dog {bark=>dogBark} does Tree {bark=>treeBark}
{...}

Then we could have code like
my Trog $foo = Trog.new();
my Dog $spot := $foo;
my Tree $willow := $foo;
$spot.bark(); # calls dogBark()
$willow.bark(); #calls treeBark()

This works better when Dog::bark and Tree::bark are both needed but they
do different things.

Joe Gottman

Luke Palmer

okunmadı,
6 Oca 2004 21:34:026.01.2004
alıcı Joe Gottman, Perl6

Renaming methods defeats the purpose of roles. Roles are like
interfaces inside-out. They guarantee a set of methods -- an interface
-- except they provide the implementation to (in terms of other,
required methods). Renaming the method destroys the interface
compatibility.

Your renaming can be done easily enough, and more clearly (IMO) with:

class Trog
does Dog does Tree {

method bark() { ... } # Explicitly remove the provided method
method dogBark() { .Dog::bark() }
method treeBark() { .Tree::bark() }
}

Luke

Jonathan Lang

okunmadı,
6 Oca 2004 22:17:196.01.2004
alıcı Perl6
Joe Gottman wrote:
> How about something like
> class Trog
> does Dog {bark=>dogBark} does Tree {bark=>treeBark}
> {...}
>
> Then we could have code like
> my Trog $foo = Trog.new();
> my Dog $spot := $foo;
> my Tree $willow := $foo;
> $spot.bark(); # calls dogBark()
> $willow.bark(); #calls treeBark()
>
> This works better when Dog::bark and Tree::bark are both needed but
> they do different things.

I'm not sure about the syntax, but it does a great job of illustrating the
aliasing approach to disambiguation. The exclusion approach might be
something like:

class Trog
does Dog
does Tree but no bark
{...}

so that, as far as class Trog is concerned, there I<is> no bark method for
Tree.

That is, include a pair of traits[1]: "no" and "alias", where "no"
modifies the class to remove the specified attributes and/or methods, and
where "alias" modifies the class to rename the specified attribute or
method; then apply those traits to the roles in question as if they were
properties[2], creating singleton roles that are used to compose the
class. This would require "but" to have a higher precedence than "is" or
"does", but I think that that might actually make sense.

The only drawback to this is that I don't see the "no" and "alias" traits
being useful except as properties: you exclude a method from a class
simply by not defining it in the first place; and if you want to use a
different name for a given method, you simply use the different name for
it. Then again, you could probably use "no" to implement something akin
to C++'s "private", where the method excluded by "no" is merely excluded
from use outside the class, while other class methods could see and use it
normally. I suppose that a similar line of reasoning would technically
apply to alias; but it's beyond my ability to see how forcing a method to
be known by a different name by non-class methods than it is by class
methods could possibly be useful.

Meanwhile, Luke was essentially pointing out that class methods take
precedence over role methods, and role methods I<can> be accessed by
explicitly specifying which role to use; so specifying a class method with
the same name as ambiguous role methods but which explicitly calls one of
them would also remove the ambiguity.

[1] by "trait", I'm referring to a thing (which might or might not be like
a role but probably isn't) which is applied to a class via C<is> and which
modifies the standard rules for how the class behaves. The more I look at
traits, the more I'm seeing them as being more sub-like than role-like:
they're defined more in terms of what they do to the class than what they
provide to the class.

[2] by "property", I'm referring to a role that is applied to a class via
C<but>, resulting in a new singleton class that C<is> the original class
and C<does> the new role. This differs from previous (and currently
official) use of the term in that I do I<not> assume that the role in
question has exactly one attribute. If someone can come up with a better
term for "something like an official property, but more general", I'd be
more than happy to adopt its use instead.


=====
Jonathan "Dataweaver" Lang

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

Jonathan Lang

okunmadı,
6 Oca 2004 22:29:556.01.2004
alıcı Luke Palmer, Joe Gottman, Perl6
Luke Palmer wrote:
> Renaming methods defeats the purpose of roles. Roles are like
> interfaces inside-out. They guarantee a set of methods -- an interface
> -- except they provide the implementation to (in terms of other,
> required methods). Renaming the method destroys the interface
> compatibility.

Not so. A role is more than an inside-out interface; it guarantees a set
of methods either by calling it an error to not define a given method in a
class that C<does> the role or by defining the method itself. In the
latter case, renaming the method can be quite useful; even in the former
case, renaming or excluding methods from a role is useful if you want an
interface which is almost, but not quite, like the one that the role
provides.

Joe Gottman

okunmadı,
6 Oca 2004 23:09:476.01.2004
alıcı Perl6

But won't explicitly removing bark() from the class also disable
Dog::bark() and Tree::bark() for the class? Renaming would work if other
methods in Dog are directed to dogBark() when they call bark(), and other
methods in Tree are redirected to treeBark().

Joe Gottman

Austin Hastings

okunmadı,
6 Oca 2004 23:45:096.01.2004
alıcı Jonathan Lang, Luke Palmer, Joe Gottman, Perl6

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


> Luke Palmer wrote:
> > Renaming methods defeats the purpose of roles. Roles are like
> > interfaces inside-out. They guarantee a set of methods -- an interface
> > -- except they provide the implementation to (in terms of other,
> > required methods). Renaming the method destroys the interface
> > compatibility.
>
> Not so. A role is more than an inside-out interface; it guarantees a set
> of methods either by calling it an error to not define a given method in a
> class that C<does> the role or by defining the method itself. In the
> latter case, renaming the method can be quite useful; even in the former
> case, renaming or excluding methods from a role is useful if you want an
> interface which is almost, but not quite, like the one that the role
> provides.

There's two ways to look at that. One way is to say: "I'm going to define an
interface as being this OTHER thing minus a method." That seems like a
positive construction, and supporting it might be desirable.

The other way is to say: "Nobody knows what methods call what other methods
in their implementation (nor should we know). Therefore, removing methods is
forbidden. If you have a conflict of methods, alias them and provide support
in the knowledge that any component C<role> that requires the method may
call it internally."

I'm in favor of the second approach, myself. If you alias away all of the
(e.g.) C<bark> methods, you must provide a replacement.

=Austin

Jonathan Lang

okunmadı,
6 Oca 2004 23:44:326.01.2004
alıcı Joe Gottman, Perl6
Joe Gottman wrote:

> Luke Palmer wrote:
> > Your renaming can be done easily enough, and more clearly (IMO) with:
> >
> > class Trog
> > does Dog does Tree {
> > method bark() { ... } # Explicitly remove the provided method
> > method dogBark() { .Dog::bark() }
> > method treeBark() { .Tree::bark() }
> > }
>
> But won't explicitly removing bark() from the class also disable
> Dog::bark() and Tree::bark() for the class?

No. method bark isn't technically being removed; rather, method
Trog::bark is being defined as "postpone the definition" while
simultaneously taking precedence over both Dog::bark and Tree::bark, since
it is a class method native to Trog while the others are role methods
provided to Trog - and class methods take precedence over role methods.
So if "my Trog $spot; $spot.bark;" is executed, perl will unambiguously
call $spot.Trog::bark, and then complain that it hasn't been defined.
(This assumes that it doesn't complain during compile-time.)

This works, but seems counterintuitive to me.

Jonathan Lang

okunmadı,
6 Oca 2004 23:58:306.01.2004
alıcı Austin_...@yahoo.com, Luke Palmer, Joe Gottman, Perl6
Austin Hastings wrote:
> There's two ways to look at that. One way is to say: "I'm going to
> define an interface as being this OTHER thing minus a method." That
> seems like a positive construction, and supporting it might be
> desirable.
>
> The other way is to say: "Nobody knows what methods call what other
> methods in their implementation (nor should we know). Therefore,
> removing methods is forbidden. If you have a conflict of methods, alias
> them and provide support in the knowledge that any component C<role>
> that requires the method may call it internally."

Or you could say that when you "exclude" a method, what you're really
doing is hiding it from everything external to where it's declared, while
leaving it available to be called internally. Method exclusion would be
more like declaring a private method in C++ than actually removing it from
the class or role. This means that a method wouldn't be provided to a
class that C<does> its role but excludes it itself, and thus it wouldn't
be used to satisfy the requirements of any other roles that the class
C<does>.

Austin Hastings

okunmadı,
7 Oca 2004 01:06:447.01.2004
alıcı Jonathan Lang, Luke Palmer, Joe Gottman, Perl6

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

> Austin Hastings wrote:
> > There's two ways to look at that. One way is to say: "I'm going to
> > define an interface as being this OTHER thing minus a method." That
> > seems like a positive construction, and supporting it might be
> > desirable.
> >
> > The other way is to say: "Nobody knows what methods call what other
> > methods in their implementation (nor should we know). Therefore,
> > removing methods is forbidden. If you have a conflict of methods, alias
> > them and provide support in the knowledge that any component C<role>
> > that requires the method may call it internally."
>
> Or you could say that when you "exclude" a method, what you're really
> doing is hiding it from everything external to where it's declared, while
> leaving it available to be called internally. Method exclusion would be
> more like declaring a private method in C++ than actually removing it from
> the class or role. This means that a method wouldn't be provided to a
> class that C<does> its role but excludes it itself, and thus it wouldn't
> be used to satisfy the requirements of any other roles that the class
> C<does>.

Huh? If it's available to be called internally, you've got the same problem
(we were talking about conflict resolution earlier, I think) -- that you
need to know which one to call.

So you alias one (Dog::bark) to dogBark, which leaves Tree::bark as the
default "bark". This is fine.

But if you say "there ain't no bark", either we should complain that doing a
Dog or a Tree demands it, or we should catch the exception at compile or run
time. I'm in favor of the first solution -- you must provide one -- since
that seems to be more in keeping with the general "role philosophy" that
Larry's been emitting.

The notion of "I'm defining it to be {...}" is an interesting one -- you're
explicitly discussing it, so obviously you've thought about it. But it's not
there. I wonder if it wouldn't be easier to say {fail} instead?

=Austin

Jonathan Lang

okunmadı,
7 Oca 2004 02:45:067.01.2004
alıcı Austin_...@yahoo.com, Luke Palmer, Joe Gottman, Perl6
Austin Hastings wrote:

> Jonathan Lang wrote:
> > Austin Hastings wrote:
> > > There's two ways to look at that. One way is to say: "I'm going to
> > > define an interface as being this OTHER thing minus a method." That
> > > seems like a positive construction, and supporting it might be
> > > desirable.
> > >
> > > The other way is to say: "Nobody knows what methods call what other
> > > methods in their implementation (nor should we know). Therefore,
> > > removing methods is forbidden. If you have a conflict of methods,
> > > alias them and provide support in the knowledge that any component
> > > C<role> that requires the method may call it internally."
> >
> > Or you could say that when you "exclude" a method, what you're really
> > doing is hiding it from everything external to where it's declared,
> > while leaving it available to be called internally. Method exclusion
> > would be more like declaring a private method in C++ than actually
> > removing it from the class or role. This means that a method wouldn't
> > be provided to a class that C<does> its role but excludes it itself,
> > and thus it wouldn't be used to satisfy the requirements of any other
> > roles that the class C<does>.
>
> Huh? If it's available to be called internally, you've got the same
> problem (we were talking about conflict resolution earlier, I think) --
> that you need to know which one to call.
>
> But if you say "there ain't no bark", either we should complain that
> doing a Dog or a Tree demands it, or we should catch the exception at
> compile or run time. I'm in favor of the first solution -- you must
> provide one -- since that seems to be more in keeping with the general
> "role philosophy" that Larry's been emitting.

Ah; I think I see the confusion. You're conflating methods that a role
supplies with methods that a role demands.

Consider this:

role Tree {
method bark() {...};
must grow(); # Tree doesn't have a grow method,
# but its class will have to have one.
};

role Dog {
method bark() {...};
must grow();
};

class Trog does Tree does Dog {
};

my Trog $spot;
$spot.bark; # what happens?
$spot.grow; # what happens?

OK: when you call $spot.bark, Trog looks for a "bark" method; it finds
two: Tree::bark and Dog::bark. Since both methods have been supplied by
roles, $spot has no idea which one to use, and throws an exception to that
effect.

When you call $spot.grow, however, Trog finds _no_ methods; Dog doesn't
supply a grow method, and neither does Tree. You get a compile-time error
to the effect of "incomplete class declaration - missing a grow method."
As such, the code given above is incorrect. Adjusting for this error, we
rewrite Trog as follows:

class Trog does Tree but no bark does Dog {
method grow() {...};
};

Now when we call $spot.bark, it finds Dog::bark but not Tree::bark;
therefore, it barks like a dog.

When we call $spot.grow, it finds Trog::grow, but there's still no
Dog::grow or Tree::grow for it to find; so it grows as only a Trog can.

Things get more interesting when one role demands a method and another
role supplies it:

role Tree {
method bark;
}

role Dog {
must bark;
method threaten { bark; }
}

class Trog does Tree does Dog {
}

my Trog $spot;
$spot.threaten; # what happens?

When $spot.threaten is called, Trog only finds Dog::threaten, and thus
calls it. Dog::threaten then calls $spot.bark; Trog finds a Tree::bark
method and nothing else, so $spot barks like a Tree.

In other words, when a method supplied by a role calls another method,
that call gets dispatched to whatever class that role is a part of.

Combining this example with the first one:

role Tree {
method bark() {...};
must grow(); # Tree doesn't have a grow method,
# but its class will have to have one.
};

role Dog {
method bark() {...};
must grow();
method threaten { bark; }
};

class Trog does Tree does Dog but no bark {
method grow() {...};
};

my Trog $spot;
$spot.threaten; # what happens?

When $spot.threaten is called, Trog only finds a Dog::threaten() method,
and dispatches the call to it; it in turn calls $spot.bark; Trog finds a
Tree::bark but not a Dog::bark (because the bark that would normally be
supplied by Dog was suppressed by Trog). There's no ambiguity, so $spot
barks like a Tree.

So what would happen in the following situation:

role Dog {
method bark() {...};
method threaten { bark; }
};

class Toothless does Dog but no bark {
}

my Toothless $rover;
$rover.threaten;

$rover.threaten calls $rover.bark; #Toothless then looks for a bark
method...

...and doesn't find one. Hmm...

This is only a problem because one of Dog's methods calls the bark method;
doing so implicitly causes the role to demand that method on a conceptual
basis. Any attribute or method that is used by any of a role's supplied
methods is implicitly demanded by that role; but the compiler cannot
easily figure out which ones those are. OTOH, do we really want to assume
that Dog demands threaten if none of Dog's other methods call it? It
would in theory be safe to exclude "threaten" from Dog, even though it
wouldn't be safe to exclude "bark".

Perhaps _threaten_ can be made to explicitly demand access to bark? That
would allow a role's demands to be composed from its methods in a manner
similar to how a class' methods are composed from its roles... something
like:

role Dog {
method bark() {...}
method threaten must bark() { bark; }
}

class Toothless does Dog but no bark() {
}

would be illegal because
Dog::threaten() must bark(), thus
Dog must bark(), thus
Toothless must bark(), but
neither Toothless nor its role can bark().

But

role Dog {
method bark() {...}
method threaten must bark() { bark; }
}

class Toothless does Dog but no threaten() {
}

would be fine; even

role Dog {
method bark() {...}
method threaten must bark() { bark; }
}

class Toothless does Dog but no threaten() no bark() {
}

would be fine (in this case, because removing threaten also removes the
demand for bark).

But in the case of

role Dog {
method bark() {...}
must bark();
method threaten must bark() { bark; }
}

class Toothless does Dog but no threaten() no bark() {
}

You'd still have a problem, because even excluding threaten doesn't
exclude Dog's demand to be able to bark.

> The notion of "I'm defining it to be {...}" is an interesting one --
> you're explicitly discussing it, so obviously you've thought about it.
> But it's not there. I wonder if it wouldn't be easier to say {fail}
> instead?

IMHO, if you're explicitly discussing it but it's not there, it should
somehow be specified as one of the role's demands - either "needs
$.attributeName" or "must methodName (signature)" sound right to me. If
you have a literal C<{...}> somewhere, you must define that specific
routine later - not merely another one that substitutes for it via
dispatching.

Piers Cawley

okunmadı,
7 Oca 2004 03:11:097.01.2004
alıcı Jonathan Lang, Luke Palmer, Joe Gottman, Perl6
Jonathan Lang <datawe...@yahoo.com> writes:

> Luke Palmer wrote:
>> Renaming methods defeats the purpose of roles. Roles are like
>> interfaces inside-out. They guarantee a set of methods -- an interface
>> -- except they provide the implementation to (in terms of other,
>> required methods). Renaming the method destroys the interface
>> compatibility.
>
> Not so. A role is more than an inside-out interface; it guarantees a set
> of methods either by calling it an error to not define a given method in a
> class that C<does> the role or by defining the method itself. In the
> latter case, renaming the method can be quite useful; even in the former
> case, renaming or excluding methods from a role is useful if you want an
> interface which is almost, but not quite, like the one that the role
> provides.

And examples of doing all of that are given in the original Traits
paper (and, for that matter, in the summary of Traits/Roles that I
wrote up for the summary.)

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

Piers Cawley

okunmadı,
7 Oca 2004 03:23:527.01.2004
alıcı Joe Gottman, Perl6

I'm really not happy with the idea that simply using a typed variable
should change the way a method call on that variable is
dispatched. You seem to be saying that

class Parent { override { "Parent" }}
class Child is Parent { override { "Child" }}

my $kid = Child.new;

my Parent $dad := $kid;

print $kid.override; # "Child"
print $dad.override; # "Parent"

And down that road lies C++ and other insanity.

Austin Hastings

okunmadı,
7 Oca 2004 11:21:597.01.2004
alıcı Jonathan Lang, Perl6
From: Jonathan Lang [mailto:datawe...@yahoo.com]

First, my understanding is that there's a shared requirement (must grow) and
a name conflict. Before this can compile, we'll need to provide a grow()
method, and we'll need to disambiguate the bark method: there can be only
one. Larry and the paper are pretty clear on this point.

So I'm thinking

class Trog does Tree does Dog { bark => dogBark } {
method grow {...};
}

> OK: when you call $spot.bark, Trog looks for a "bark" method; it finds
> two: Tree::bark and Dog::bark. Since both methods have been supplied by
> roles, $spot has no idea which one to use, and throws an exception to that
> effect.

I am uncomfortable with this. My understanding has been that the will be a
compile time error, caught when the class declaration is made. Has that
changed recently?

> When you call $spot.grow, however, Trog finds _no_ methods; Dog doesn't
> supply a grow method, and neither does Tree. You get a compile-time error
> to the effect of "incomplete class declaration - missing a grow method."

I sort-of agree. I don't think it requires a call to grow for the compile
time error -- I think it's a declaration time error. (I suppose that the
declarator could provide a "secret" {...} declaration for all required
methods, but what value does this have?)

> As such, the code given above is incorrect. Adjusting for this error, we
> rewrite Trog as follows:
>
> class Trog does Tree but no bark does Dog {
> method grow() {...};
> };

Modulo syntax, this is more right, yes.

> Now when we call $spot.bark, it finds Dog::bark but not Tree::bark;
> therefore, it barks like a dog.

Yep.

> When we call $spot.grow, it finds Trog::grow, but there's still no
> Dog::grow or Tree::grow for it to find; so it grows as only a Trog can.

Yep.

> Things get more interesting when one role demands a method and another
> role supplies it:
>
> role Tree {
> method bark;
> }
>
> role Dog {
> must bark;
> method threaten { bark; }
> }
>
> class Trog does Tree does Dog {
> }
>
> my Trog $spot;
> $spot.threaten; # what happens?
>
> When $spot.threaten is called, Trog only finds Dog::threaten, and thus
> calls it. Dog::threaten then calls $spot.bark; Trog finds a Tree::bark
> method and nothing else, so $spot barks like a Tree.

Right. This seems sensible.

> In other words, when a method supplied by a role calls another method,
> that call gets dispatched to whatever class that role is a part of.
>
> Combining this example with the first one:
>
> role Tree {
> method bark() {...};
> must grow(); # Tree doesn't have a grow method,
> # but its class will have to have one.
> };
>
> role Dog {
> method bark() {...};
> must grow();
> method threaten { bark; }
> };
>
> class Trog does Tree does Dog but no bark {
> method grow() {...};
> };
>
> my Trog $spot;
> $spot.threaten; # what happens?
>
> When $spot.threaten is called, Trog only finds a Dog::threaten() method,
> and dispatches the call to it; it in turn calls $spot.bark; Trog finds a
> Tree::bark but not a Dog::bark (because the bark that would normally be
> supplied by Dog was suppressed by Trog). There's no ambiguity, so $spot
> barks like a Tree.

Good so far.

> So what would happen in the following situation:
>
> role Dog {
> method bark() {...};
> method threaten { bark; }
> };
>
> class Toothless does Dog but no bark {
> }
>
> my Toothless $rover;
> $rover.threaten;
>
> $rover.threaten calls $rover.bark; #Toothless then looks for a bark
> method...
>
> ...and doesn't find one. Hmm...
>
> This is only a problem because one of Dog's methods calls the bark method;
> doing so implicitly causes the role to demand that method on a conceptual
> basis. Any attribute or method that is used by any of a role's supplied
> methods is implicitly demanded by that role; but the compiler cannot
> easily figure out which ones those are. OTOH, do we really want to assume
> that Dog demands threaten if none of Dog's other methods call it? It
> would in theory be safe to exclude "threaten" from Dog, even though it
> wouldn't be safe to exclude "bark".

I agree that this is the question.

And this seems to be a valid way to get around the problem.

> But in the case of
>
> role Dog {
> method bark() {...}
> must bark();
> method threaten must bark() { bark; }
> }
>
> class Toothless does Dog but no threaten() no bark() {
> }
>
> You'd still have a problem, because even excluding threaten doesn't
> exclude Dog's demand to be able to bark.

"Hit jist haint a dawg ifn hit dont bark."

But large roles are going to want to say C<must> just once, since (1) that
makes for easier, more legible documentation; and (2) if chromatic is
correct, most functionality with roles (smaller than classes, recall) will
likely be pretty incestuous.

So I'm still a little uncomfortable stripping bits out without replacing
them. I think I'd rather see a no-op instead.

=Austin

Jonathan Lang

okunmadı,
7 Oca 2004 19:36:367.01.2004
alıcı Austin_...@yahoo.com, Perl6
Austin Hastings wrote:
> Jonathan Lang wrote:
> > OK: when you call $spot.bark, Trog looks for a "bark" method; it finds
> > two: Tree::bark and Dog::bark. Since both methods have been supplied
> > by roles, $spot has no idea which one to use, and throws an exception
> > to that effect.
>
> I am uncomfortable with this. My understanding has been that the will be
> a compile time error, caught when the class declaration is made. Has
> that changed recently?

I don't know for certain; but I think so. Given the ambiguities that can
crop up with multiple dispatches and signatures, it may not be feasable to
catch all of them at compile-time.

> > As such, the code given above is incorrect. Adjusting for this error,
> > we rewrite Trog as follows:
> >
> > class Trog does Tree but no bark does Dog {
> > method grow() {...};
> > };
>
> Modulo syntax, this is more right, yes.

I'm not familiar with modulo. Also, I'm probably abusing the "but" syntax
like nobody's business here.

> > But in the case of
> >
> > role Dog {
> > method bark() {...}
> > must bark();
> > method threaten must bark() { bark; }
> > }
> >
> > class Toothless does Dog but no threaten() no bark() {
> > }
> >
> > You'd still have a problem, because even excluding threaten doesn't
> > exclude Dog's demand to be able to bark.
>
> "Hit jist haint a dawg ifn hit dont bark."

You've never met my dog.

> But large roles are going to want to say C<must> just once, since (1)
> that makes for easier, more legible documentation; and (2) if chromatic
> is correct, most functionality with roles (smaller than classes, recall)
> will likely be pretty incestuous.

The "must" clause in the above "threaten" method is redundant, an artifact
of cut-n-paste coding. The example would work exactly the same with or
without it.

0 yeni ileti