Perl

NAVIGATION
CATEGORIES
REFERRENCE
LINKS
  • class interface of roles

    29 answers - 1150 bytes - related search similar search Add To My Delicious Add To My Stumble Upon Add To My Google Mark Add To My Facebook Add To My Digg Add To My Reddit

    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.

Re: class interface of roles


max 4000 letters.
Your nickname that display:
In order to stop the spam: 3 + 2 =
QUESTION ON "Perl"

EMSDN.COM