class interface of roles
29 answers - 1150 bytes -

H,
After re-reading about the typing of mixins in
I wonder how the example would look like in Perl6.
Here is what I think it could look like:
role GenEqual
{
method equal( : GenEqual $ Bool ) {}
}
role GenPointMixin
{
has Int $.x;
has Int $.y;
method equal( ::?CLASS GenEqual $self: ::?CLASS $p Bool )
{
return super.equal(p) and # <-- handwave
self.x == $p.x and self.y == $p.y;
}
}
class GenSquare does GenEqual does GenPointMixin
{
has Int $.side;
method equal ( : ::?CLASS $p Bool )
{
return self.side == $p.side;
}
}
The handwave part is the interface of the composed role to
the class it is composed into and the typing of this interface.
The article proposes to expand the mixin self type prior to the
class interface. The latter then poses type constraints onto the
class. Do roles work like that in Perl6? I mean would the approach
of the article of using two F-bounded quantifications (see the last
formular in section 8) be a valid type model for class composition?
Regards, TSa.
--
No.1 | | 472 bytes |
| 
H,
is this subject not of interest? I just wanted to start a
discussion about the class composition process and how a
role designer can require the class to provide an equal
method and then augment it to achieve the correct behavior.
Contrast that with the need to do the same in every class
that gets the equal method composed into if the role doesn't
have a superclass interface as described in the article.
Regards,
--
No.2 | | 634 bytes |
| 
TSa wrote:
H,
is this subject not of interest? I just wanted to start a
discussion about the class composition process and how a
role designer can require the class to provide an equal
method and then augment it to achieve the correct behavior.
Contrast that with the need to do the same in every class
that gets the equal method composed into if the role doesn't
have a superclass interface as described in the article.
This will be the same as requiring that a class implements a method,
except the method's name is infix:<(::T $self: T $other) or some such.
Sam.
No.3 | | 1517 bytes |
| 
Sam Vilain wrote:
TSa wrote:
>is this subject not of interest? I just wanted to start a
>discussion about the class composition process and how a
>role designer can require the class to provide an equal
>method and then augment it to achieve the correct behavior.
>Contrast that with the need to do the same in every class
>that gets the equal method composed into if the role doesn't
>have a superclass interface as described in the article.
>
This will be the same as requiring that a class implements a method,
except the method's name is infix:<(::T $self: T $other) or some such.
How does a Role require that the target class implement a method (or
do another Role)?
Does the "class GenSquare does GenEqual does GenPointMixin" line imply
an ordering of class composition? This would seem to be required for
the super.equal hand-wave to work but part of the Traits Paper goodness
came from avoiding an ordering. Composition is just order insensitive
flattening. Conflicts like the equal method in the P have to be
explicitly resolved in the target class, either using aliases
or fully qualified names. So there's no super needed.
I would like a way to make one Role to require that the target class
"does" another abstract Role, is there already such a technique?
(I should confess that I haven't yet read the P linked article),
Brad
No.4 | | 2913 bytes |
| 
10/2/06, Brad Bowman <list (AT) bereft (DOT) netwrote:
Sam Vilain wrote:
TSa wrote:
>is this subject not of interest? I just wanted to start a
>discussion about the class composition process and how a
>role designer can require the class to provide an equal
>method and then augment it to achieve the correct behavior.
>Contrast that with the need to do the same in every class
>that gets the equal method composed into if the role doesn't
>have a superclass interface as described in the article.
>>
>
This will be the same as requiring that a class implements a method,
except the method's name is infix:<(::T $self: T $other) or some such.
How does a Role require that the target class implement a method (or
do another Role)?
IIRC, it simply needs to provide a method stub, like so:
method bar { }
This will tell the class composer that this method must be created
before everything is finished.
Does the "class GenSquare does GenEqual does GenPointMixin" line imply
an ordering of class composition? This would seem to be required for
the super.equal hand-wave to work but part of the Traits Paper goodness
came from avoiding an ordering. Composition is just order insensitive
flattening. Conflicts like the equal method in the P have to be
explicitly resolved in the target class, either using aliases
or fully qualified names. So there's no super needed.
The "super" in a Role should be late bound, so will have no relevance
when inside the role, but only make sense when composed into a class.
This is probably one of the more confusing points of roles I think.
As for how the example in the P might work, I would suspect that
"super" would not be what we are looking for here, but instead a
variant of "next METHD". However, even with that an ordering of some
kind is implied.
I suppose this is again where the different concepts of classes are
roles can get very sticky. I have always look at roles, once composed
into the class, as no longer needing to exist. In fact, if it weren't
for the idea of runtime role compostion and runtime role
introspection, I would say that roles themselves could be garbage
collected at the end of the compile time cycle.
I would like a way to make one Role to require that the target class
"does" another abstract Role, is there already such a technique?
I am not familiar with one, but I have had this need as well lately in
using Moose roles. We have a concept in Moose (stolen from the
Fortress language) where a particular role can exclude the use of
another role, but not the ability to require it, although I see no
reason why it couldn't be done.
- Stevan
No.5 | | 3717 bytes |
| 
Brad Bowman wrote:
Hi,
Did you mean to go off list?
No, I didn't.
Jonathan Lang wrote:
Brad Bowman wrote:
>Does the "class GenSquare does GenEqual does GenPointMixin" line imply
>an ordering of class composition?
>
No. This was a conscious design decision: the order in which you
compose roles has no effect on the result.
Great. That's what I want.
>
>I would like a way to make one Role to require that the target class
>"does" another abstract Role, is there already such a technique?
>
What's wrong with just having the role compose the other abstract role
itself? That is, instead of "role Bar requires Baz; class Foo does
Bar does Baz", why not say "role Bar does Baz; class Foo does Bar"?
--
That would work, as long as you get a compile time error when
Foo doesn't implement the Baz abstract interface.
which is exactly what happens. Mind you, the compile-time error
won't report that Baz::method is unimplemented, or even that
Bar::method is unimplemented; it will report that Foo::method is
unimplemented. It's up to the programmer to figure out where
Foo::method came from, on the off chance that it matters.
It's perhaps also less clear that the indirect Baz mixin must be
implemented.
If Bar does Baz, you can read that as "Bar requires Baz to be
implemented, too."
There's a tendency, when dealing with traditional inheritance systems,
to think of the primary function of a superclass as being a supplier
of implementations for any classes that inherit from it. I submit
that this is the wrong way to think of roles: rather, a role is first
and foremost a source of interface requirements for whatever does that
role. If a role includes a method declaration, that should be read as
"anything that does this role must provide a method that matches this
one's name and signature." If a role does another role, that should
be read as "anything that does this role should conform to the
requirements of this other role as well." Any implementations that a
role provides should be viewed as _sample_ implementations, to be used
if and only if you can find no reason not to use them.
BTW, this includes attributes: if a role declares a public attribute,
this should be read as that role requiring an accessor method for that
attribute; if whatever does the role redefines the methods (including
the accessor method) such that none of them refer to the attribute,
the attribute should not be mixed in. If a role defines a private
attribute and then fails to define any methods that access that
attribute, the only way that that attribute should end up getting
mixed into something else is if whatever does the role that the
attribute is in provides the methods in question. In summary,
attributes and method bodies in roles should be taken as _suggestions_
- only the public method names and signatures should be viewed as
requirements.
I guess any role could just declare some yada methods and leave it
at that.
That too.
Bear in mind that when you compose a role into another role, you are
under no obligation to replace yada methods with defined ones. In
fact, it's even conceivable for you to replace a defined method with a
yada method if the default implementation from the other role isn't
suited to the current one. The only time that you're required to
replace yada methods with defined methods is when you compose into a
class.
No.6 | | 3105 bytes |
| 
Stevan Little wrote:
Brad Bowman wrote:
How does a Role require that the target class implement a method (or
do another Role)?
IIRC, it simply needs to provide a method stub, like so:
method bar { }
This will tell the class composer that this method must be created
before everything is finished.
Correct.
I suppose this is again where the different concepts of classes are
roles can get very sticky. I have always look at roles, once composed
into the class, as no longer needing to exist. In fact, if it weren't
for the idea of runtime role compostion and runtime role
introspection, I would say that roles themselves could be garbage
collected at the end of the compile time cycle.
Again, you've hit the nail on the head. To elaborate on this a little
bit, the only reason that perl needs to keep track of a role hierarchy
at all is for parameter matching purposes (if Foo does Bar and Bar
does Baz, Foo can be used if a signature asks for Baz).
I would like a way to make one Role to require that the target class
"does" another abstract Role, is there already such a technique?
I am not familiar with one, but I have had this need as well lately in
using Moose roles. We have a concept in Moose (stolen from the
Fortress language) where a particular role can exclude the use of
another role, but not the ability to require it, although I see no
reason why it couldn't be done.
As I mentioned before, having role Bar require that Baz also be
composed is a simple matter of saying "role Bar does Baz".
This notion of exclusionary roles is an interesting one, though. I'd
like to hear about what kinds of situations would find this notion
useful; but for the moment, I'll take your word that such situations
exist and go from there.
I wonder if it would be worthwhile to extend the syntax of roles so
that you could prepend a "no" on any declarative line, resulting in a
compilation error any time something composing that role attempts to
include the feature in question. So, for instance, you might have
role Bar {
no method baz (Num, Str);
}
class Foo does Bar {
method baz (Num $n, Str $s) { } # compilation error: Bar
forbade this method!
}
or
role Bar no does Baz { # granted, the english grammar is all wrong
}
class Foo does Bar does Baz { # compilation error: Bar forbade the
inclusion of Baz!
}
This is not the same as removing something that a composed role
brought in, which is a separate potentially useful notion. The former
is "Foo doesn't play well with Bar; so don't try to use them
together"; the latter is "Foo can do _almost_ everything Bar can, but
not quite." Mind you, if I ever see something to the effect of "Foo
does Bar except baz()" as valid syntax, I'll expect a query to the
effect of "Foo does Bar?" to answer to the negative. This would
include "can Foo be used when Bar is asked for?"
No.7 | | 628 bytes |
| 
Monday 02 2006 08:58, Jonathan Lang wrote:
I wonder if it would be worthwhile to extend the syntax of roles so
that you could prepend a "no" on any declarative line, resulting in a
compilation error any time something composing that role attempts to
include the feature in question. *So, for instance, you might have
* * role Bar {
* * * * no method baz (Num, Str);
* * }
* * class Foo does Bar {
* * * * method baz (Num $n, Str $s) { } # compilation error: Bar
forbade this method!
* * }
This feels like the false-cognate problem waiting to creep back in.
-- c
No.8 | | 852 bytes |
| 
10/2/06, Jonathan Lang <dataweaver (AT) gmail (DOT) comwrote:
This notion of exclusionary roles is an interesting one, though. I'd
like to hear about what kinds of situations would find this notion
useful; but for the moment, I'll take your word that such situations
exist and go from there.
Well to be honest, I haven't found a real-world usage for it yet (at
least in my travels so far), but the Fortress example was this:
trait Molecule extends Molecule
excludes { InorganicMolecule }
end
trait InorganicMolecule extends Molecule end
And from that I could see that given a large enough set of roles you
would surely create roles which conflicted with one another on a
conceptual level rather then on a methods/attribute (i.e. - more
concrete) level.
- Stevan
No.9 | | 1999 bytes |
| 
H,
Brad Bowman wrote:
Sam Vilain wrote:
>This will be the same as requiring that a class implements a
>method, except the method's name is infix:<(::T $self: T $other)
>or some such.
Sure. The point is, how does a role designer mix in the x and y
coordinate attributes *and* augment the notion of equality to
encompass these new attributes *without* shifting this burden onto
the class implementor!
Does the "class GenSquare does GenEqual does GenPointMixin" line
imply an ordering of class composition? This would seem to be
required for the super.equal hand-wave to work but part of the Traits
Paper goodness came from avoiding an ordering. Composition is just
order insensitive flattening. Conflicts like the equal method in the
P have to be explicitly resolved in the target class, either using
aliases or fully qualified names. So there's no super needed.
Hmm, my aim was more at the class composition process as such.
I envision a type bound calculation for all composed roles. This
bound then is available as super to all roles. Inter-role conflicts
are orthogonal to this. If the bound is fulfilled the order of
role composition doesn't matter. In the case of the equality checks
the different participants of the composition call each other and
use logical and to yield the final equality check. To the outside
world the equality method appears to be a single call on the type
of the objects created from the class. Note that it is type correct
in the sense that all participants are considered. My hope is that
this is achieved automatically as outcome of the composition process
and not by intervention of the class implementor.
(I should confess that I haven't yet read the P linked article),
To understand it you might actually need to read previous articles
in the series, too.
Regards,
--
No.10 | | 1980 bytes |
| 
H,
Stevan Little wrote:
As for how the example in the P might work, I would suspect that
"super" would not be what we are looking for here, but instead a
variant of "next METHD".
I'm not familiar with the next METHD syntax. How does one get the
return value from it and how are parameters passed? Would the respective
line in the equal method then read:
return next METHD($p) and self.x == $p.x and self.y == $p.y;
I think that a super keyword might be nice syntactic sugar for this.
However, even with that an ordering of some
kind is implied
The only ordering I see is that the class is "up" from the role's
perspective. When more then one role is combined and all require
the presence of an equal method I think the roles can be combined
in any order and the super refers to the class combined so far.
IW, at any given time in the composition process there is a current
version of the class' method. The final outcome is a method WALK
or however this is called in composition order. Conceptually this
is method combination: seen from outside the class has just one
type correct method equal. Theoretical background can be found in
I suppose this is again where the different concepts of classes are
roles can get very sticky. I have always look at roles, once composed
into the class, as no longer needing to exist. In fact, if it weren't
for the idea of runtime role compostion and runtime role
introspection, I would say that roles themselves could be garbage
collected at the end of the compile time cycle.
I see that quite different: roles are the primary carrier of type
information! Dispatch depends on a partial ordering of roles. I
think all roles will form a type lattice that is available at
runtime for type checks. With parametric roles there will be dynamic
instanciations as needed.
Regards,
--
No.11 | | 1501 bytes |
| 
H,
Stevan Little wrote:
10/2/06, Jonathan Lang <dataweaver (AT) gmail (DOT) comwrote:
>This notion of exclusionary roles is an interesting one, though. I'd
>like to hear about what kinds of situations would find this notion
>useful; but for the moment, I'll take your word that such situations
>exist and go from there.
Well to be honest, I haven't found a real-world usage for it yet (at
least in my travels so far), but the Fortress example was this:
trait Molecule extends Molecule
excludes { InorganicMolecule }
end
trait InorganicMolecule extends Molecule end
Wouldn't that be written in Perl6 the other way around?
role Molecule {}
role InorganicMolecule {}
role Molecule does Molecule ^ InorganicMolecule {}
Which is a nice usage of the xor role combinator.
And from that I could see that given a large enough set of roles you
would surely create roles which conflicted with one another on a
conceptual level rather then on a methods/attribute (i.e. - more
concrete) level.
I don't abide to that. If roles are conceptually modelling the same
entity their vocabulary should conflict also. Well unless some
differing coding conventions accidentally produce non-conflicting
roles. The whole point of type systems relies on the fact that
concrete conflicts indicate conceptual ones!
Regards,
--
No.12 | | 1467 bytes |
| 
TSa wrote:
I'm not familiar with the next METHD syntax.
It's simple: if a multi method says "next METHD;" then execution of
the current method gets aborted, and the next MMD candidate is tried;
it uses the same parameters that the current method used, and it
returns its value to the current method's caller. In effect, "next
METHD" is an aspect of MMD that allows an individual method to say
"I'm not the right guy for this job", and to punt to whoever's next in
line. If not for the possibility of side effects that occur before
the punt, one could pretend that the current method was never tried in
the first place.
I see that quite different: roles are the primary carrier of type
information! Dispatch depends on a partial ordering of roles. I
think all roles will form a type lattice that is available at
runtime for type checks.
True: the relationships between various roles and classes ("who does
what?") is needed for runtime type checking. However, the _contents_
of the roles are only important for composing classes and for the
occasional runtime introspection of a role. If roles are never
composed or inspected at runtime, the only details about them that
need to be kept are "who does what?" - and if all type-checking takes
place at compile-time, not even this is needed.
But now we're getting dangerously close to perl6internals territory
No.13 | | 3928 bytes |
| 
10/6/06, TSa <Thomas.Sandlass (AT) barco (DOT) comwrote:
H,
Stevan Little wrote:
As for how the example in the P might work, I would suspect that
"super" would not be what we are looking for here, but instead a
variant of "next METHD".
I'm not familiar with the next METHD syntax. How does one get the
return value from it and how are parameters passed? Would the respective
line in the equal method then read:
return next METHD($p) and self.x == $p.x and self.y == $p.y;
I think that a super keyword might be nice syntactic sugar for this.
I think super is not something we would want in Roles, it implies
ordering, which defeats the flattening aspect of Roles. IIRC the
syntax to call the next method with new args and a return value is
this:
return call($p) and self.x == $p.x and self.y == $p.y;
However, I am not sure I really like the look of that myself.
However, even with that an ordering of some
kind is implied
The only ordering I see is that the class is "up" from the role's
perspective.
But thats the whole problem, there is no "up" in roles, they are flattened.
When more then one role is combined and all require
the presence of an equal method I think the roles can be combined
in any order and the super refers to the class combined so far.
IW, at any given time in the composition process there is a current
version of the class' method. The final outcome is a method WALK
or however this is called in composition order. Conceptually this
is method combination: seen from outside the class has just one
type correct method equal. Theoretical background can be found in
I do not think method combination should be the default for role
composition, it would defeat the composeability of roles because you
would never have conflicts.
However, I can see the possibility of method combinations in roles
being some kind of special case. How that might look from a syntactic
perspective I have no idea.
I suppose this is again where the different concepts of classes are
roles can get very sticky. I have always look at roles, once composed
into the class, as no longer needing to exist. In fact, if it weren't
for the idea of runtime role compostion and runtime role
introspection, I would say that roles themselves could be garbage
collected at the end of the compile time cycle.
I see that quite different: roles are the primary carrier of type
information!
Well yes, they do seem to have taken on this role ;). However, roles
as originally envisioned in the Traits paper are not related to the
type system, but instead related to class/object system. In fact the
Trait paper gave it's examples in Smalltalk, which is not a strongly
typed language (unless you count the idea that *everything* is an
object and therefore that is their type).
I think we need to be careful in how we associate roles with the type
system and how we assocaite them with the object system. I worry that
they will end up with conflicting needs and responsibilities and roles
will end up being too complex to be truely useful.
Dispatch depends on a partial ordering of roles.
Type based dispatch does (MMD), but class based method dispatch
doesn't need it at all.
The whole fact that dispatching requires roles to be partially ordered
actually tells me that maybe roles should not be so hinged to the type
system since roles are meant to be unordered.
Possiblely we should be seeing roles as a way of *implementing* the
types, and not as a core component of the type system itself?
I did some experimentation with this in Moose::Autobox, but that is
for Perl 5 so I am not sure how relevant it is here.
- Stevan
No.14 | | 3211 bytes |
| 
10/6/06, TSa <Thomas.Sandlass (AT) barco (DOT) comwrote:
H,
Stevan Little wrote:
10/2/06, Jonathan Lang <dataweaver (AT) gmail (DOT) comwrote:
>This notion of exclusionary roles is an interesting one, though. I'd
>like to hear about what kinds of situations would find this notion
>useful; but for the moment, I'll take your word that such situations
>exist and go from there.
>
Well to be honest, I haven't found a real-world usage for it yet (at
least in my travels so far), but the Fortress example was this:
trait Molecule extends Molecule
excludes { InorganicMolecule }
end
trait InorganicMolecule extends Molecule end
Wouldn't that be written in Perl6 the other way around?
role Molecule {}
role InorganicMolecule {}
role Molecule does Molecule ^ InorganicMolecule {}
Which is a nice usage of the xor role combinator.
Well, it does seem to accomplish a similar goal. However, in your
example there is nothing about the Molecule which will prevent
me from composing it with the IMolecule, which was the primary
goal of the Fortress example. In addition in the Fortress example the
Molecule role is not coupled to the and Inorganic molecules,
in your example they are.
But IMH this is just another example of TIMTWTDI, because your
example achieves similar goals and would likely be a valid design
approach as well.
And from that I could see that given a large enough set of roles you
would surely create roles which conflicted with one another on a
conceptual level rather then on a methods/attribute (i.e. - more
concrete) level.
I don't abide to that. If roles are conceptually modelling the same
entity their vocabulary should conflict also. Well unless some
differing coding conventions accidentally produce non-conflicting
roles. The whole point of type systems relies on the fact that
concrete conflicts indicate conceptual ones!
But part of the power of role composability is that the role itself
does not need to dictate what class it is composed into. So conceptual
conflicts cannot be determined until compostion actually occurs, and
conflicts between two conceptually conflicting roles cannot be
detected until composition time either. And of course there is nothing
to say that two conceptually conflicting roles have a concrete
conflict either (either between methods or attributes).
I think that maybe we need to seperate the concept of roles as types
and roles as partial classes, they seem to me to be in conflict with
one another. And even they are not in conflict with one another, I
worry they will bloat the complexity of roles usage.
My experiences thus far with roles in Moose have been that they can be
a really powerful means of reuse. I point you towards Yuval Kogman's
latest work on Class::Workflow, which is basically a loose set of
roles which can be composed into a highly customizable workflow
system. This is where I see the real power of roles coming into play.
- Stevan
No.15 | | 168 bytes |
| 
TSa wrote:
Dispatch depends on a partial ordering of roles.
Could someone please give me an example to illustrate what is meant by
"partial ordering" here?
No.16 | | 636 bytes |
| 
Jonathan~
10/7/06, Jonathan Lang <dataweaver (AT) gmail (DOT) comwrote:
TSa wrote:
Dispatch depends on a partial ordering of roles.
Could someone please give me an example to illustrate what is meant by
"partial ordering" here?
Sets demonstrate partial ordering. Let < denote the subset relation ship.
If A < B and B < C, then A < C for any A, B, and C.
However, it is not necessarily the case that A < B, or B < A, or B ==
A for any particular A and B.
Thus transitivity is preserved, but there is not a guarantee of
comparability between elements.
Matt
No.17 | | 1821 bytes |
| 
H,
Stevan Little wrote:
I think that maybe we need to seperate the concept of roles as types
and roles as partial classes, they seem to me to be in conflict with
one another. And even they are not in conflict with one another, I
worry they will bloat the complexity of roles usage.
The bloat aside I believe it is essential to have roles as the key
players of the type system. I propose to handle the typeish aspect of
roles as described in the paper I linked to: there should be a trait
'is augmented' that is applicable to a role and to methods in a role.
In such a method a call to next METHD should invoke the class's method.
Alternatively we could make the method combination behavior the default
and have a class method trait 'is disambig' or 'is override' for the
case where the class needs to have the final word.
Note that the superclass interface of roles should be mostly inferred
from the usage of next METHD. As such it is a useful guidance for
error reports in the class composition process.
My experiences thus far with roles in Moose have been that they can be
a really powerful means of reuse. I point you towards Yuval Kogman's
latest work on Class::Workflow, which is basically a loose set of
roles which can be composed into a highly customizable workflow
system. This is where I see the real power of roles coming into play.
Is this a set of free mixins or are they dependent on the class to
provide a certain interface to fully achieve the means of the roles?
I also consider roles a powerful tool but I believe that the type
system should play a role in the composition process that goes beyond
simple checking of name clashes.
Regards,
--
No.18 | | 1511 bytes |
| 
H,
Jonathan Lang wrote:
TSa wrote:
Dispatch depends on a partial ordering of roles.
Could someone please give me an example to illustrate what is meant by
"partial ordering" here?
In addition to Matt Fowles explanation I would like to
give the following example lattice build from the roles
role A { has $.x }
role B { has $.y }
role C { has $.z }
There can be the four union combined roles A|B, A|C, B|C
and A|B|C which complete the type lattice:
Any={}
/ | \
/ | \
/ | \
/ | \
/ | \
A={x} B={y} C={z}
| \ / \ / |
| \ / \ / |
| \/ \/ |
| /\ /\ |
| / \ / \ |
| / \ / \ |
A|B={x,y} A|C={x,z} B|C={y,z}
\ | /
\ | /
\ | /
\ | /
\ | /
A|B|C={x,y,z}
Note that A = (A|B) & (A|C) is the intersection type of A|B and A|C.
Note further that A|B is a subtype of A and B written A|B <: A and
A|B <: B and so on. Usually the A|B|C is called Bottom or some such.
I think it is the Whatever type of Perl6. It is the glb (greatest lower
bound) of A, B and C. In a larger lattice this type gets larger as well.
Any is the trivial supertype of all types.
This lattice can then be used for type checks and specificity
comparisons in dispatch. BTW, such a lattice can be calculated lazily
from any set of packages. In pure MMD the selected target has to be
the most specific in all dispatch relevant positions.
Regards,
--
No.19 | | 573 bytes |
| 
H,
TSa wrote:
Note that the superclass interface of roles should be mostly inferred
from the usage of next METHD. As such it is a useful guidance for
error reports in the class composition process.
Actually 'next METHD' doesn't catch all superclass interface issues.
There is the simple case of calling e.g. accessor methods on super
which should result in the requirement to provide them. So I still
propose a super keyword that in roles means the object as seen from
the uncomposed class.
Regards,
--
No.20 | | 3755 bytes |
| 
H,
Stevan Little wrote:
I do not think method combination should be the default for role
composition, it would defeat the composeability of roles because you
would never have conflicts.
I don't get that. The type system would give compile time errors.
The current spec means that in case of a "conflicting" method the
class version simply overrides the role version. That is there is
simple, short or long name based "conflict" checking with priority
to the class.
I see that quite different: roles are the primary carrier of type
information!
Well yes, they do seem to have taken on this role ;).
If it is not roles that carry type information then the Perl6 type
system is as of now completely unspecced. Even with roles I miss
some clear statements about their theoretical background.
However, roles
as originally envisioned in the Traits paper are not related to the
type system, but instead related to class/object system. In fact the
Trait paper gave it's examples in Smalltalk, which is not a strongly
typed language (unless you count the idea that *everything* is an
object and therefore that is their type).
Remember the paper did not include state for traits and thus nicely
avoided several typing issues particularly in SmallTalk that is based
on single inheritance and dispatch along these lines.
I think we need to be careful in how we associate roles with the type
system and how we assocaite them with the object system. I worry that
they will end up with conflicting needs and responsibilities and roles
will end up being too complex to be truely useful.
My current understanding is that properly typed roles can obliviate
the need of the things described in theory.pod and directly go with
F-bounded polymorphism as the theoretical model of the type system.
It e.g. is strong enough to model Haskell type classes. Note that there
are free mixins that pose no requirements on the class in the theory
as described in the article.
Dispatch depends on a partial ordering of roles.
Type based dispatch does (MMD), but class based method dispatch
doesn't need it at all.
I strongly agree. The two hierarchies should be separated. The
only interweaving that occurs is the class composition process.
And this process should IMH be directed by the type system and
provide for method combination when the correct typing of the role
requires it. Typical examples that need method combination are
equality checking, sorting support and generic container types.
There seems to be another connection from the class hierarchy to
the role hierarchy that is that a class has a role of the same
name so that class names can be used where a type is expected or
however this is supposed to work. In the end there shall be some
mixing of class and type based dispatch or some kind of embedding
of the class dispatch into type dispatch.
The whole fact that dispatching requires roles to be partially ordered
actually tells me that maybe roles should not be so hinged to the type
system since roles are meant to be unordered.
But how else do we define a subtype relation if not through a role
type lattice?
Possiblely we should be seeing roles as a way of *implementing* the
types, and not as a core component of the type system itself?
Hmm, again what is the type system then? All indications from the
Synopsis and this list go for roles taking over the responsibility
of key player in the type department. E.g. type parameters also go
with roles not with classes.
Regards,
--
No.21 | | 782 bytes |
| 
H,
TSa wrote:
Note that A = (A|B) & (A|C) is the intersection type of A|B and A|C.
Note further that A|B is a subtype of A and B written A|B <: A and
A|B <: B and so on. Usually the A|B|C is called Bottom or some such.
I think it is the Whatever type of Perl6. It is the glb (greatest lower
bound) of A, B and C. In a larger lattice this type gets larger as well.
Any is the trivial supertype of all types.
Damn it! I always puzzle glb and lub (least upper bound). So it should
read lub there. This is because the intension set gets larger even
though a subtype is formed. Note that the extension set "the instances"
becomes smaller! In a limiting case the Whatever type has got no
instances at all :)
Sorry,
--
No.22 | | 1892 bytes |
| 
TSa wrote:
TSa wrote:
>Note that the superclass interface of roles should be mostly inferred
>from the usage of next METHD. As such it is a useful guidance for
>error reports in the class composition process.
Actually 'next METHD' doesn't catch all superclass interface issues.
There is the simple case of calling e.g. accessor methods on super
which should result in the requirement to provide them. So I still
propose a super keyword that in roles means the object as seen from
the uncomposed class.
I think the word "super" is already to overloaded to be used for this
purpose. Setting that aside for now
Do you mean that super.blah() appearing in a method defined in a role A
should require that all classes which do A need to provide a blah
implementation? (or produce a composition time error if they don't)
I'd prefer to have a declarative mechanism for specifying such requirements.
Firstly for self-documenting clarity, there's no need to scan for a "super",
and secondly because eval and super.$method_name would allow runtime failures.
Some alternatives appeared elsewhere in this thread but it's unclear whether
they would produce error at composition time or at runtime. (yada methods)
The same applies to the "next METHD" inference suggested:
Note that the superclass interface of roles should be mostly inferred
from the usage of next METHD.
A Role should be able to say "to do this Role you need to implement these
methods" and have a compile/composition time error if not.
(There does need to be a way to call, in a Role A, both the "blah" defined
in A and whatever the "blah" the final class may use. $self.blah() is the
later, $self.A::blah() or similar is likely to be the former.)
Brad
No.23 | | 2680 bytes |
| 
Brad Bowman wrote:
TSa wrote:
TSa wrote:
>Note that the superclass interface of roles should be mostly inferred
>from the usage of next METHD. As such it is a useful guidance for
>error reports in the class composition process.
>
Actually 'next METHD' doesn't catch all superclass interface issues.
There is the simple case of calling e.g. accessor methods on super
which should result in the requirement to provide them. So I still
propose a super keyword that in roles means the object as seen from
the uncomposed class.
What do you mean by "uncomposed class"?
I think the word "super" is already to overloaded to be used for this
purpose. Setting that aside for now
Do you mean that super.blah() appearing in a method defined in a role A
should require that all classes which do A need to provide a blah
implementation? (or produce a composition time error if they don't)
I hope not; that's exactly what declaring an unimplemented method in a
role is supposed to do. (And declaring an implemented method does the
same thing, with the addition that it also suggests an implementation
that the class is free to use or ignore as it sees fit.)
The same applies to the "next METHD" inference suggested:
Note that the superclass interface of roles should be mostly inferred
from the usage of next METHD.
A Role should be able to say "to do this Role you need to implement these
methods" and have a compile/composition time error if not.
Agreed.
(There does need to be a way to call, in a Role A, both the "blah" defined
in A and whatever the "blah" the final class may use. $self.blah() is the
later, $self.A::blah() or similar is likely to be the former.)
No, there doesn't. Given that C<class Foo does Aand C<role A does
B>, There needs to be a way to call, in class Foo, both the "blah"
defined in Foo, the "blah" defined in A (so that Foo can reimplement
A's version as a different method or as part of its own), and the
"blah" defined in B; and there needs to be a way to call, in role A,
both the "blah" defined in Foo and the "blah" defined B; but role A
does not need a way to explicitly call a method defined in A. It
should assume that if Foo overrides A's implementation of blah, Foo
knows what it's doing; by the principle of least surprise, Foo should
never end up overriding A's implementation of blah only to find that
the original implementation is still being used by another of the
methods acquired from A.
No.24 | | 2134 bytes |
| 
TSa wrote:
Jonathan Lang wrote:
TSa wrote:
Dispatch depends on a partial ordering of roles.
Could someone please give me an example to illustrate what is meant by
"partial ordering" here?
In addition to Matt Fowles explanation I would like to
give the following example lattice build from the roles
role A { has $.x }
role B { has $.y }
role C { has $.z }
There can be the four union combined roles A|B, A|C, B|C
and A|B|C which complete the type lattice:
Any={}
/ | \
/ | \
/ | \
/ | \
/ | \
A={x} B={y} C={z}
| \ / \ / |
| \ / \ / |
| \/ \/ |
| /\ /\ |
| / \ / \ |
| / \ / \ |
A|B={x,y} A|C={x,z} B|C={y,z}
\ | /
\ | /
\ | /
\ | /
\ | /
A|B|C={x,y,z}
So if I'm reading this right, a class that does both A and B should be
"lower" in the partial ordering than a class that does just one or the
other. And if A does B, then you'll never have a class that does just
A without also doing B, which trims out a few possible nodes and paths
from the lattice for practical purposes:
Any={}
| \
| \
| \
| \
| \
B={y} C={z}
/ \ |
/ \ |
/ \ |
/ \ |
/ \ |
/ \ |
A|B={x,y} B|C={y,z}
\ /
\ /
\ /
\ /
\ /
A|B|C={x,y,z}
I note that while the lattice is related to whatever role hierarchies
may or may not exist, it is not the same as them. In particular,
roles that have no hierarchal relationship to each other _will_ exist
in the same lattice. In fact, all roles will exist in the same
lattice, on the first row under "Any". Right? does the fact that
"A does B" mean that A would be placed where "A|B" is, and "A|C" would
end up in the same node as "A|B|C"?
This lattice can then be used for type checks and specificity
comparisons in dispatch. BTW, such a lattice can be calculated lazily
from any set of packages. In pure MMD the selected target has to be
the most specific in all dispatch relevant positions.
By "most specific", you'd mean "closest to the top"?
No.25 | | 4708 bytes |
| 
H,
Jonathan Lang wrote:
What do you mean by "uncomposed class"?
The self always refers to the object as instance of the composed
class. Methods are therefore resolving to the outcome of the
composition process. But super in a role refers to methods from
the class definition even when the final method comes from method
combination in the composition process.
>I think the word "super" is already to overloaded to be used for
>this purpose. Setting that aside for now
Better ideas? Is there a super keyword already? do you mean
overloaded in general and type speak?
>Do you mean that super.blah() appearing in a method defined in a
>role A should require that all classes which do A need to provide a
>blah implementation? (or produce a composition time error if they
>don't)
Yes, this is exactly what I mean with superclass interface of roles.
I hope not; that's exactly what declaring an unimplemented method in
a role is supposed to do.
My idea is that the type system calculates a type bound for the class
from the definition of the role. That includes attributes and their
accessor methods. It's a matter of taste how explicit this interface
is declared or how much of it is infered.
(And declaring an implemented method does
the same thing, with the addition that it also suggests an
implementation that the class is free to use or ignore as it sees
fit.)
We have a priority conflict here. The question is if the class or the
role is seeing the other's methods for method combination.
>A Role should be able to say "to do this Role you need to implement
>these methods" and have a compile/composition time error if not.
Agreed.
I agree as well. But on a wider scope then just method provision.
>(There does need to be a way to call, in a Role A, both the "blah"
>defined in A and whatever the "blah" the final class may use.
Yes, this is the subject of the current debate. I'm opting for a
method combination semantics that allows the role to call the class
method.
>$self.blah() is the later, $self.A::blah() or similar is likely to
>be the former.)
No, there doesn't. Given that C<class Foo does Aand C<role A does
B>, There needs to be a way to call, in class Foo, both the "blah"
defined in Foo, the "blah" defined in A (so that Foo can reimplement
A's version as a different method or as part of its own), and the
"blah" defined in B;
From the class all composed parts are available through namespace
qualified names. But a role is a classless and instanceless entity.
The self refers to the objects created from the composed class. The role
is not relevant in method dispatch. That is a method is never dispatched
to a role. But the role should be able to participate in the method
definition of the composed class.
and there needs to be a way to call, in role A,
both the "blah" defined in Foo and the "blah" defined B; but role A
does not need a way to explicitly call a method defined in A.
I'm not sure if I get this right. But as I said above a role can not
be dispatched to. Which method do you think should take precedence
the role's or the class's? That is who is the defining entity in the
method combination process? I would hope it is the role if a as of now
unknown syntax has declared it. Perhaps it should be even the default.
The rational for my claim is that a role is composed several times
and then every class doing the role automatically gets the correct
version. all classes are burdened with caring for the role's
part in the method.
It
should assume that if Foo overrides A's implementation of blah, Foo
knows what it's doing; by the principle of least surprise, Foo should
never end up overriding A's implementation of blah only to find that
the original implementation is still being used by another of the
methods acquired from A.
Could you make an example because I don't understand what you mean with
original implementation and how that would be used by role methods.
Method dispatch is on the class never on the role. As far as dispatch is
concerned the role is flattend out. But the question is how the class's
method is composed in the first place.
Regards,
--
No.26 | | 2248 bytes |
| 
H,
Jonathan Lang wrote:
So if I'm reading this right, a class that does both A and B should be
"lower" in the partial ordering than a class that does just one or the
other. And if A does B, then you'll never have a class that does just
A without also doing B, which trims out a few possible nodes and paths
from the lattice for practical purposes:
Any={}
| \
| \
| \
| \
| \
B={y} C={z}
/ \ |
/ \ |
/ \ |
/ \ |
/ \ |
/ \ |
A|B={x,y} B|C={y,z}
\ /
\ /
\ /
\ /
\ /
A|B|C={x,y,z}
Correct. The lattice is a structural analysis of the roles.
I note that while the lattice is related to whatever role hierarchies
may or may not exist, it is not the same as them. In particular,
roles that have no hierarchal relationship to each other _will_ exist
in the same lattice. In fact, all roles will exist in the same
lattice, on the first row under "Any". Right?
Yes, if they are disjoined structurally. intersection roles
appear as nodes under Any.
does the fact that
"A does B" mean that A would be placed where "A|B" is, and "A|C" would
end up in the same node as "A|B|C"?
To get at the node labeled A|B above you either need a definition
role A does B { has $.x }
or an outright full definition
role A { has $.x; has $.y }
So, yes the node should be called A and A|C coincides with A|B|C.
I'm not sure if this ordering of roles can be called duck typing
because it would put roles that have the same content into the
same lattice node. The well known bark method of Dog and Tree
comes to mind. But the arrow types of the methods will be different.
has type :(Dog Dog) the other :(Tree Tree) and a joined
node will have type :(Dog&Tree Dog|Tree). This might just give
enough information to resolve the issues surrounding the DogTree
class.
By "most specific", you'd mean "closest to the top"?
No, closer to the bottom. The join operator | of the lattice produces
subtypes with a larger interface that is more specific. It's like
the more derived class in a class hierarchy.
Regards, TSa.
--
No.27 | | 8271 bytes |
| 
TSa wrote:
Jonathan Lang wrote:
What do you mean by "uncomposed class"?
The self always refers to the object as instance of the composed
class. Methods are therefore resolving to the outcome of the
composition process. But super in a role refers to methods from
the class definition even when the final method comes from method
combination in the composition process.
Still not following. Can you give an example?
I hope not; that's exactly what declaring an unimplemented method in
a role is supposed to do.
My idea is that the type system calculates a type bound for the class
from the definition of the role. That includes attributes and their
accessor methods. It's a matter of taste how explicit this interface
is declared or how much of it is inferred.
Note that it's entirely possible for attributes to not make it into
the final class, if the accessor methods get redefined in such a way
as to remove reference to the attributes. This is part of the notion
that roles supply an outline of what the class should do, but only the
class actually supplies the definitive details of how to do it.
As I see it: "is" and "does" declarations in a role impose
requirements on the final class: it must derive from another class
("is"), or it must compose another role ("does"). "method" and "has"
are each one part requirement and one part suggestion: for "method",
the class is required to include a method that matches the given name
(which, of course, includes the method's signature), and a particular
closure is suggested that the class can accept or override.
For "has", the class is required to provide accessor methods
corresponding to the attribute's read/write capabilities (a rw method
if it's a rw attribute; a regular method if it's a regular attribute;
and no method if it's a private attribute); like any other method, a
closure is suggested that the class may accept or override. In
addition, the role suggests that a given attribute be added to the
class' state information. This suggestion is implicitly accepted if
any of the methods that are used by the final class refer to the
attribute; it is implicitly rejected if none of them do. The same
rule applies to private methods.
Thus, the only things that I'd recommend using to calculate a
type-boundary would be the superclasses (provided by "is") and the
method names (provided by "method" and "has").
(And declaring an implemented method does
the same thing, with the addition that it also suggests an
implementation that the class is free to use or ignore as it sees
fit.)
We have a priority conflict here. The question is if the class or the
role is seeing the other's methods for method combination.
I believe that I address this later on; if not, please clarify.
>(There does need to be a way to call, in a Role A, both the "blah"
>defined in A and whatever the "blah" the final class may use.
>
Yes, this is the subject of the current debate. I'm opting for a
method combination semantics that allows the role to call the class
method.
Agreed.
>$self.blah() is the later, $self.A::blah() or similar is likely to
>be the former.)
>
No, there doesn't. Given that C<class Foo does Aand C<role A does
B>, There needs to be a way to call, in class Foo, both the "blah"
defined in Foo, the "blah" defined in A (so that Foo can reimplement
A's version as a different method or as part of its own), and the
"blah" defined in B;
From the class all composed parts are available through namespace
qualified names. But a role is a classless and instanceless entity.
The self refers to the objects created from the composed class. The role
is not relevant in method dispatch. That is a method is never dispatched
to a role. But the role should be able to participate in the method
definition of the composed class.
Also agreed. In particular, I'm referring to _how_ a role should
participate in the method definition of the composed class; I am not
referring to method dispatch, which is limited to the class hierarchy.
and there needs to be a way to call, in role A,
both the "blah" defined in Foo and the "blah" defined B; but role A
does not need a way to explicitly call a method defined in A.
I'm not sure if I get this right. But as I said above a role can not
be dispatched to. Which method do you think should take precedence
the role's or the class's? That is who is the defining entity in the
method combination process?
I agree with the idea behind the current definition of this: if the
class provides its own definition for a method, that should take
precedence over the role's definition. If it doesn't, then it adopts
the role's definition as its own.
I would hope it is the role if a as of now
unknown syntax has declared it. Perhaps it should be even the default.
The rational for my claim is that a role is composed several times
and then every class doing the role automatically gets the correct
version. all classes are burdened with caring for the role's
part in the method.
Huh?
It
should assume that if Foo overrides A's implementation of blah, Foo
knows what it's doing; by the principle of least surprise, Foo should
never end up overriding A's implementation of blah only to find that
the original implementation is still being used by another of the
methods acquired from A.
Could you make an example because I don't understand what you mean with
original implementation and how that would be used by role methods.
role A {
method foo() { say "Ah" }
method bar() { $self.foo() }
}
class Foo does A { }
class Bar does A {
method foo() { say "" }
}
class Baz does A {
method foo() {
$self.A::foo();
say "Choo!";
}
method baz() { $self.A::foo() }
}
The above should be exactly equivalent to:
class Foo {
method foo() { say "Ah" }
method bar() { $self.foo() }
}
class Bar {
method foo() { say "" }
method bar() { $self.foo() }
}
class Baz {
method foo() {
$self!'A::foo'();
say "Choo!";
}
method bar() { $self.foo() }
method baz() { $self!'A::foo'() }
my method 'A::foo'() { say "Ah" }
}
(The optimizer should remove the extraneous curly braces.)
So:
Foo.foo(); # "Ah\n"
Foo.bar(); # "Ah\n"
Bar.foo(); # "\n"
Bar.bar(); # "\n"
Baz.foo(); # "Ah\nChoo!\n"
Baz.bar(); # "Ah\nChoo!\n"
Baz.baz(); # "Ah\n"
In Baz, C<my method 'A::foo'()represents the original implementation
of foo(), while C<method foo()represents the final implementation
chosen by the class.
If you were to allow a role method to directly refer to its own
implementation, you could do something like:
role A {
method foo() { say "Ah" }
method bar() { $self.A::foo() }
}
Substituting this version of role A into my original example would
lead to this sort of output:
Foo.foo(); # "Ah\n"
Foo.bar(); # "Ah\n"
Bar.foo(); # "\n"
Bar.bar(); # "Ah\n"
Baz.foo(); # "Ah\nChoo!\n"
Baz.bar(); # "Ah\n"
Baz.baz(); # "Ah\n"
reflection, this wouldn't neccessarily be as bad as I originally
thought. I don't see why anyone would want to use it; but it's
identified for what it is clearly enough that someone reading the role
will know what to expect when he starts making changes to things.
Method dispatch is on the class never on the role. As far as dispatch is
concerned the role is flattend out. But the question is how the class's
method is composed in the first place.
Agreed. Hopefully, the above sheds some light on where I'm coming from.
No.28 | | 8646 bytes |
| 
H,
Jonathan Lang wrote:
Still not following. Can you give an example?
The example in the original post contains a class GenSquare that
has got an equal method that checks the sides of the self square
and the incoming argument square. The GenPointMixin role provides
an equal method that compares the x and y coordinates. The correct
final method equal of the composed class needs to check sides and
coordinates. If the simple side checking equal of the class
overrides the closure from the role there is a false implementation.
TH, forcing all classes to call the role closure is bad design of
the composition process. Hence I'm arguing for a super keyword that
in a role refers to the class. In the example super.equal($p) calls
the side checking closure.
Note that it's entirely possible for attributes to not make it into
the final class, if the accessor methods get redefined in such a way
as to remove reference to the attributes. This is part of the notion
that roles supply an outline of what the class should do, but only the
class actually supplies the definitive details of how to do it.
I see that you regard the class as the ultimate definer of
functionality. The role requires a method including its signature
but the class implements it. For the type system the role encodes
the guarantee of the method availability.
As I see it: "is" and "does" declarations in a role impose
requirements on the final class: it must derive from another class
("is"), or it must compose another role ("does"). "method" and "has"
are each one part requirement and one part suggestion: for "method",
the class is required to include a method that matches the given name
(which, of course, includes the method's signature), and a particular
closure is suggested that the class can accept or override.
I almost agree here. The only thing I'm asking for is that the role's
closure is not discarded so easily. The designer of the role takes
responsibility for the role's part in the final method closure. The
combination process should produce the correct result automatically
and independent of the class's cooperation.
Thus, the only things that I'd recommend using to calculate a
type-boundary would be the superclasses (provided by "is") and the
method names (provided by "method" and "has").
That makes classes too typeish. The type system is mostly based on
roles. The class inheritance graph should not be used for typing.
do you want constraints on the class derivation process that
guarantees subclasses to be subtypes? As I read it the class derivation
is free to violate replaceability of subclasses where superclasses
are expected. Roles are a guarantee of functionality not classes.
I agree with the idea behind the current definition of this: if the
class provides its own definition for a method, that should take
precedence over the role's definition. If it doesn't, then it adopts
the role's definition as its own.
>I would hope it is the role if a as of now
>unknown syntax has declared it. Perhaps it should be even the default.
>The rational for my claim is that a role is composed several times
>and then every class doing the role automatically gets the correct
>version. all classes are burdened with caring for the role's
>part in the method.
Huh?
Yeah! The role adds a certain aspect to the correct implementation of
a method. And so does the class. But it is the role that is composed
into the class not the other way around. A role is intended to be
composed several times into completely different classes. With blind
precedence to class methods the role's aspects are lost and have to
be reintroduced in each and every class. I consider that inconvenient
and error prone.
>It
>should assume that if Foo overrides A's implementation of blah, Foo
>knows what it's doing; by the principle of least surprise, Foo should
>never end up overriding A's implementation of blah only to find that
>the original implementation is still being used by another of the
>methods acquired from A.
>>
>Could you make an example because I don't understand what you mean with
>original implementation and how that would be used by role methods.
role A {
method foo() { say "Ah" }
method bar() { $self.foo() }
}
Isn't that self.foo() without the sigil? It is clear that .foo
is dispatched on the class.
[]
class Baz {
method foo() {
$self!'A::foo'();
say "Choo!";
}
method bar() { $self.foo() }
method baz() { $self!'A::foo'() }
my method 'A::foo'() { say "Ah" }
}
What is A referring to here? Baz doesn't compose role A here.
And why the exclamation mark?
In Baz, C<my method 'A::foo'()represents the original implementation
of foo(), while C<method foo()represents the final implementation
chosen by the class.
K, thanks for the example.
If you were to allow a role method to directly refer to its own
implementation, you could do something like:
role A {
method foo() { say "Ah" }
method bar() { $self.A::foo() }
}
Yes, this should call the role closure from within the class.
With precedence to class methods my original example should read
role GenPointMixin
{
has Int $.x;
has Int $.y;
method class_equal ( : ::?CLASS $p ) {}
method equal( : ::?CLASS $p Bool )
{
return self.class_equal(p) and
self.x == $p.x and self.y == $p.y;
}
}
which is clumsy and relies on the fact that the class is *not*
overriding the equal method but of course provides the class_equal
method. I want that combination process to be available under the
equal name slot. Which requires the role to take precedence at least
for the equal method. The question is what syntax to use for this
feature. I propose a 'is augmented' trait on the role method.
Re: Partial
>I'm not sure if this ordering of roles can be called duck typing
>because it would put roles that have the same content into the
>same lattice node. The well known bark method of Dog and Tree
>comes to mind.
IIRC, duck typing is based on how well the set of available method
names match, its main flaw coming from the possibility of homonymous
methods. With the Dog and Tree example, consider the possibility that
both versions of "bark" take no parameters other than the invocant,
and neither version returns anything. Their signatures would thus be
identical (since the invocant's type is the final class' type).
Conceptually the methods take and return the invocant type. This
is why I gave them as :(Dog Dog) and :(Tree Tree) respectively.
That's what I meant with arrow types. But Void as return type is fine
as well. The question is now how these two signatures are merged
together to form the signature required from the class disambiguation.
The glb Dog&Tree to me means that a common supertype be implemented
in the class. Hmm, this at least is what contravariant arrow types
demand. But I'm as of now puzzled if it's not better to require the
class to implement the lub Dog|Tree based on the idea that this makes
instances of the class subtypes of both interfaces. The implementation
of which obliges to the class. Well, invocant parameters are covariant.
So, yes it should be a method on Dog|Tree the type of instances of a
class that does Dog and Tree.
>But the arrow types of the methods will be different.
>has type :(Dog Dog) the other :(Tree Tree) and a joined
>node will have type :(Dog&Tree Dog|Tree). This might just give
>enough information to resolve the issues surrounding the DogTree
>class.
Not sure what you mean by "the arrow types". Are you referring to
embedding a return type in the signature?
I mean arrow types as used in formal treatments of type systems.
Functions are denoted with arrows there. This is also the reason why
we have the in signatures.
Regards, TSa.
--
No.29 | | 11325 bytes |
| 
TSa wrote:
H,
Jonathan Lang wrote:
Still not following. Can you give an example?
The example in the original post contains a class GenSquare that
has got an equal method that checks the sides of the self square
and the incoming argument square. The GenPointMixin role provides
an equal method that compares the x and y coordinates. The correct
final method equal of the composed class needs to check sides and
coordinates. If the simple side checking equal of the class
overrides the closure from the role there is a false implementation.
Actually, the correct equal method for the composed class needs to
check the sides of the square, because how the class defines it: in
this example, class GenSquare _is_ the final class, and so whatever
method it provides _is_ the final method, for good or ill. If this
results in other method definitions inherited from GenPointMixin
behaving strangely, then GenSquare needs to override those methods as
well.
Mind you, the above implies a radical shift in the underlying
semantics, akin to having class Dog compose role Tree and redefining
method bark() to suit its own nature. Proper use of role composition
is to refine the role's underlying concept, not to radically alter it.
Even if we were to go with a modified version of the original
example, where GenSquare is a role and both GenSquare and GenPoint are
being composed into a final class - say, "GenPositionedSquare" - you
still have the problem that the final class is conceptually supposed
to be both a GenSquare and a GenPoint at the same time. If the
underlying concepts behind the composed roles are fundamentally
incompatable (such as a Dog and a Tree), you're going to have
problems.
TH, forcing all classes to call the role closure is bad design of
the composition process. Hence I'm arguing for a super keyword that
in a role refers to the class. In the example super.equal($p) calls
the side checking closure.
This is what happens by default; no special syntax is needed.
Note that it's entirely possible for attributes to not make it into
the final class, if the accessor methods get redefined in such a way
as to remove reference to the attributes. This is part of the notion
that roles supply an outline of what the class should do, but only the
class actually supplies the definitive details of how to do it.
I see that you regard the class as the ultimate definer of
functionality. The role requires a method including its signature
but the class implements it. For the type system the role encodes
the guarantee of the method availability.
Exactly.
As I see it: "is" and "does" declarations in a role impose
requirements on the final class: it must derive from another class
("is"), or it must compose another role ("does"). "method" and "has"
are each one part requirement and one part suggestion: for "method",
the class is required to include a method that matches the given name
(which, of course, includes the method's signature), and a particular
closure is suggested that the class can accept or override.
I almost agree here. The only thing I'm asking for is that the role's
closure is not discarded so easily. The designer of the role takes
responsibility for the role's part in the final method closure. The
combination process should produce the correct result automatically
and independent of the class's cooperation.
Perl has yet to implement a telepathic compiler, so the compiler can
only make best guesses as to what the programmer intended. For now,
we have to make some concessions to reality; one such concession is
that the most specific thing should have authority over (and
responsibility for) the less specific things. The thing being
composed into is by definition more specific than the thing being
composed when it comes to implementation; thus, the latter should be
given free rein to redefine the methods provided by the former - and
if things go wrong, you should blame the latter rather than the
former. Letting a role override a decision made by a class that it's
composed into means that there's something about the role's
implementation that the class cannot touch.
Thus, the only things that I'd recommend using to calculate a
type-boundary would be the superclasses (provided by "is") and the
method names (provided by "method" and "has").
That makes classes too typeish. The type system is mostly based on
roles. The class inheritance graph should not be used for typing.
It is a fact that class inheritance affects the behaviour of a class;
it cannot be disregarded when considering type.
That said, class inheritance (with respect to its semantic differences
from role composition) is an area where my understanding is rather
shaky. To me, the whole notion of inheritance is a bit too typeish
for my tastes; I try to handle _all_ code reuse by means of roles,
with classes only being brought into play when I want to be able to
instantiate an object. That is, I tend to define what an object is
exclusively by what it does.
BTW, whoever writes the Perl 6 reference book has a golden opportunity
to highlight a bit of conceptual elegance. In perl 6, there are three
ways to handle code reuse: "is" (class inheritance), "does" (role
composition), and "has" (attribute delegation).
do you want constraints on the class derivation process that
guarantees subclasses to be subtypes?
I'm not sure what you mean here. In particular, Perl uses "subtype"
in a very specific way - and which is antithetical to "subclass".
roles and subclasses expand an object's capabilties; subtypes restrict
them.
As I read it the class derivation
is free to violate replaceability of subclasses where superclasses
are expected. Roles are a guarantee of functionality not classes.
What do you mean by "replaceability of subclasses"?
>I would hope it is the role if a as of now
>unknown syntax has declared it. Perhaps it should be even the default.
>The rational for my claim is that a role is composed several times
>and then every class doing the role automatically gets the correct
>version. all classes are burdened with caring for the role's
>part in the method.
>
Huh?
Yeah! The role adds a certain aspect to the correct implementation of
a method. And so does the class. But it is the role that is composed
into the class not the other way around. A role is intended to be
composed several times into completely different classes.
I'm with you so far
With blind
precedence to class methods the role's aspects are lost and have to
be reintroduced in each and every class. I consider that inconvenient
and error prone.
That's where you're losing me. I don't see how "blind" precedence to
class methods would cause the results that you describe.
>
>It
>should assume that if Foo overrides A's implementation of blah, Foo
>knows what it's doing; by the principle of least surprise, Foo should
>never end up overriding A's implementation of blah only to find that
>the original implementation is still being used by another of the
>methods acquired from A.
>>
>Could you make an example because I don't understand what you mean with
>original implementation and how that would be used by role methods.
>
role A {
method foo() { say "Ah" }
method bar() { $self.foo() }
}
Isn't that self.foo() without the sigil? It is clear that .foo
is dispatched on the class.
No; it's $self with the sigil. $self is the scalar that contains the
invocant.
[]
class Baz {
method foo() {
$self!'A::foo'();
say "Choo!";
}
method bar() { $self.foo() }
method baz() { $self!'A::foo'() }
my method 'A::foo'() { say "Ah" }
}
What is A referring to here? Baz doesn't compose role A here.
And why the exclamation mark?
Note the single quotes around C<A::foo>. 'A' isn't referring to
anything; it's literally part of the name.
The exclamation mark indicates that I'm accessing a private method.
Likewise, the 'my' means that I'm _defining_ a private method.
If you were to allow a role method to directly refer to its own
implementation, you could do something like:
role A {
method foo() { say "Ah" }
method bar() { $self.A::foo() }
}
Yes, this should call the role closure from within the class.
I still don't see a purpose for it.
With precedence to class methods my original example should read
role GenPointMixin
{
has Int $.x;
has Int $.y;
method class_equal ( : ::?CLASS $p ) {}
method equal( : ::?CLASS $p Bool )
{
return self.class_equal(p) and
self.x == $p.x and self.y == $p.y;
}
}
which is clumsy and relies on the fact that the class is *not*
overriding the equal method but of course provides the class_equal
method.
K; I just tried to decipher your original example. If I'm
understanding it correctly, what you're actually wanting to say is
this:
role GenEqual
{
method equal( GenEqual $p Bool ) {}
}
role GenPoint
{
has Int $.x;
has Int $.y;
method equal( GenPoint $self: GenPoint $p Bool )
{
return $self.x == $p.x and $self.y == $p.y;
}
}
class GenSquare does GenPoint does GenEqual
{
has Int $.side;
method equal ( GenSquare $p Bool )
{
return $self.GenPoint::equal($p) and $self.side == $p.side;
}
}
Note that GenPoint no longer makes any reference to GenSquare.
Re: Partial
>I'm not sure if this ordering of roles can be called duck typing
>because it would put roles that have the same content into the
>same lattice node. The well known bark method of Dog and Tree
>comes to mind.
>
IIRC, duck typing is based on how well the set of available method
names match, its main flaw coming from the possibility of homonymous
methods. With the Dog and Tree example, consider the possibility that
both versions of "bark" take no parameters other than the invocant,
and neither version returns anything. Their signatures would thus be
identical (since the invocant's type is the final class' type).
Conceptually the methods take and return the invocant type.
They don't have to; and the duck-typing problem occurs most
prominantly when they don't.
The question is now how these two signatures are merged
together to form the signature required from the class disambiguation.
Signatures shouldn't be merged.