Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Re: Solving compositional problems with Perl 6 roles

by fergal (Chaplain)
on Aug 23, 2004 at 08:53 UTC ( [id://385027]=note: print w/replies, xml ) Need Help??


in reply to Solving compositional problems with Perl 6 roles

with the idea of manipulating inheritance trees based upon an instance instead of a class (and all of the ridiculous problems that would bring.)
Cecil does this and seems to do it very nicely, although I've never used it, I just read their language specification.

I'm curious about this bit of code

$elf does Thief; # hmm ... that reads funny
is this attaching the methods at run time or at compile time? That is, do the methods stay attached to the object inside $elf or is it only treated as doing Thief in scope of this declaration? The second case would not be very useful and the first case basically amounts to "manipulating inheritance trees based upon an instance".

Replies are listed 'Best First'.
Re^2: Solving compositional problems with Perl 6 roles
by kappa (Chaplain) on Aug 23, 2004 at 12:34 UTC
    There're two syntaxes proposed in the Apocalypse 12. $elf does Thief make that very $elf a Thief. And $elf but Theif creates a cloned elf which becomes a Thief. I eagerly await writing something like:
    my $parser = new XML::Parser but Simple; # or my $mail = new MIME but Lite;
    :)
Re^2: Solving compositional problems with Perl 6 roles
by dragonchild (Archbishop) on Aug 23, 2004 at 12:37 UTC
    The first case doesn't manipulate the inheritance trees based upon an instance. For one thing, the Perl6 model won't affect instance2 just because instance1 does Foo. That is probably the biggest difference.

    The second thing is that the trait Thief will not override methods that already exist in the inheritance tree of $elf. It will add the Thief methods as a fallback, should they be needed. And, it does it until you remove the Thief trait. A12 has a lot to say on the topic, which is good.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

      The first case doesn't manipulate the inheritance trees based upon an instance. For one thing, the Perl6 model won't affect instance2 just because instance1 does Foo. That is probably the biggest difference.

      That's exactly what I would call manipulating the inheritance tree based upon an instance. The only other meaning I can think of (and I think it's the meaning you thought I meant) is that twiddling instance1 has an effect on all other instances. This would be an extremely pointless feature and isn't what I was talking about.

      So to quote Ovid again (the whole sentence this time)

      However, you can't have Elf subclass from Thief because not all elves are thieves unless you want to start toying with the idea of manipulating inheritance trees based upon an instance instead of a class (and all of the ridiculous problems that would bring.)
      that reads to me like individual objects would have their own inheritance trees so some Elfs would subclass from Thief and some wouldn't. He calls this ridiculous but isn't that exactly what Perl 6 does?

        Well, I think I goofed on this one. From reading A12, it appears that anonymous classes are built and bound to objects that use mixins. I missed that point and now it seems like we're going to have inheritance problems again. When I posted a question about this to the Perl 6 language list, Larry Wall responded:

        That's just a problem with run-time mixins in general if you can't know in advance that you're going to want both roles. That's why we encourage as much of that to be done at compile time as possible.

        This does seem like it will cause more design issues, but Larry and TheDamian both had ways around this. They suggested strategies such as creating anonymous classes or anonymous roles that could disambiguate methods as needed. I guess that makes sense as magician thieves would want the magic users spell-casting ability but the thief's fighting ability and those seem like rules that might have to be decided at compile time.

        You can read the Perl 6 language discussion here.

        Cheers,
        Ovid

        New address of my CGI Course.

        I think the relevant part of A12 is:

        Class Composition with Roles

        Objects have many kinds of relationships with other objects. One of the pitfalls of the early OO movement was to encourage people to model many relationships with inheritance that weren't really "isa" relationships. Various languages have sought to redress this deficiency in various ways, with varying degrees of success. With Perl 6 we'd like to back off a step and allow the user to define abstract relationships between classes without committing to a particular implementation.

        More specifically, we buy the argument of the Traits paper that classes should not be used both to manage objects and to manage code reuse. It needs to be possible to separate those concerns. Since a lot of the code that people want to reuse is that which manages non-isa object relationships, that's what we should abstract out from classes.

        That abstraction we are calling a role. Roles can encompass both interface and implementation of object relationships. A role without implementation degenerates to an interface. A role without interface degenerates to privately instantiated generics. But the typical role will provide both interface and at least a default implementation.

        Unlike the Traits paper, we will allow state as part of our implementation. This is necessary if we are to abstract out the delegation decision. We feel that the decision to delegate rather than compose a sub-object is a matter of implementation, and therefore that decision should be encapsulated (or at least be allowed to be encapsulated) in a role. This allows you to refactor a problem by redefining one or more roles without having to doctor all the classes that make use of those roles. This is a great way to turn your huge, glorious "god object" into a cooperating set of objects that know how to delegate to each other.

        As in the Traits paper, roles are composed at class construction time, and the class composer does some work to make sure the composed class is not unintentionally ambiguous. If two methods of the same name are composed into the same class, the ambiguity will be caught. The author of the class has various remedies for dealing with this situation, which we'll go into below.

        From the standpoint of the typical user, a role just looks like a "smart" include of a "partial class". They're smart in that roles have to be well behaved in certain respects, but most of the time the naive user can ignore the power of the abstraction.

        The enboldened (my highlighting) part in the last paragraph forms the mental model that I am using.

        In effect, the "smart include" means that there is no inheritance involved--although another part of a12 goes on to say that it maybe implemented using a form of MI at the Parrot level--the Role's methods actually become a part of the Class(es) to which they are applied when used in the compile-time form. Where the does is applied to the class itself, and applies to all instances of that class.

        In the runtime form, where the Role is applied to an instance of some class, only that instance gains the new methods. In implementation, a new anonymous class is created that is the composition of that instance' class and the Role.

        I take this to mean that if a Role is applied to an instance of class Dog at runtime, and the the Class Dog was subsequently modified (through introspection for example), the instance that had the Role applied at runtime, would continue to be based on the original version, not the modified version of Dog.

        In the end, I doubt that these details will have any great significance in use. I can imagine there being a Storable Role that know how to copy an instance' attributes to and from disk without needing to know much of anything about the Class of instance to which it is applied. If you have have (or get from somewhere) a class that does what you need, but you need to be able to save and restore the instances, you apply the Storable Role to it. At compile time of all instances need it. At runtime if only some instances need it.

        The Storable Role would be defined as an interface (or perhaps with a minimal implementation). Then you (your site) provide an implementation of the defined interface that meets your requirements: Flat files, Berkeley, RDBMS of some flavour, OODB. The interface might be defined to have two methods: .freeze :id; and .thaw :id;. Switching DB vendors becomes a case of installing their implementation of the Storable Role, and everything else continues as is.

        Or maybe that would be better implemented as a Trait? There is so much in A12 it's hard to put your finger on what wil become best practice.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://385027]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (4)
As of 2024-03-19 03:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found