Raphael Descamps wrote:
> In the original traits paper the aliasing is not "deep": to respect the
> flattening property, the semantic of the role must not change, so
> aliasing a recursive method will call the original method. It's a known
> theoretical weakness of the traits paper and "freezing roles" try to
> solve this problem.

It's a problem that doesn't exist if you don't alias.  However, you
run into another problem; namely, what to do if two roles provide
semantically incompatible definitions for the same method.  To be
fair, ailasing doesn't solve the problem either, for the reasons that
I outlined in my last post (i.e., aliasing breaks the interface).  And
"freezing roles" doesn't solve the problem either; it just specifies
which role is broken in the combined interface.  As far as I can tell,
there are only two solutions that actually solve the problem: don't
compose two roles that have incompatible methods, or find a way for
the incompatible definitions to coexist under the same name.

The former approach works off of the theory that if the names are the
same, the semantics ought to be compatible; and thus incompatible
semantics are a sign of poor design of the base roles.  In an
environment where the programmer has the ability to rewrite everything
with which he's dealing, this makes a great deal of sense.  But as
Richard pointed out, CPAN is a counterexample to this: it is
unreasonable to assume that two modules imported from CPAN, written in
isolation by different authors, will never provide conflicting roles
due to nothing more than conflicting naming conventions - roles that,
in concept, ought to be able to be used together.

As I understand things, Richard's proposed solution is to alias one of
the offending methods during the import, effectively rewriting the
source module to use a different name for the offending method, for
the sole purpose of exporting to the target application.  IMHO, this
only works if you follow the chain of compositions all the way and
alias everything.  That is:

   role Foo { method x; }
   role Bar does Foo { method x; }
   role Baz does Foo { method x; }

If you want to alias Bar.x on import, there should be an implicit
aliasing of Foo.x as well, which would lead to the implicit aliasing
of Baz.x too.  It's the only way to avoid broken interfaces: you need
to change all related interfaces to remain compatible with the one
that you change, both up and down the composition chain.  Needless to
say, this strikes me as impractical, due to the effort involved in
figuring out what needs to be aliased and what doesn't.


Another possibility would be to borrow a page from XML Namespaces,
which addressed a similar problem: allow the programmer to require
imported elements to be referenced in terms of the module from which
they were imported.  E.g.:

    use Kennel prefix Foo; # imports role Dog
    use Forest prefix Bar; # imports role Tree
    class Dogwood does Foo-Dog does Bar-Tree  { ... }
    my $dogwood is Dogwood;
    $dogwood.Foo-bark;
    $dogwood.Bar-bark;

The idea here is that "prefix Foo" and "prefix Bar" cause every name
that gets imported from that module to be prefixed with that string.
So class Dogwood wouldn't have a "bark" method: it would have a
"Foo-bark" method and a "Bar-bark" method.  IOW, the above would be
equivalent to:

    role Foo-Dog { ... method Foo-bark { ... } ... }
    role Bar-Tree { ... method Bar-bark { ... } ... }
    class Dogwood does Foo-Dog does Bar-Tree  { ... }
    my $dogwood is Dogwood;
    $dogwood.Foo-bark;
    $dogwood.Bar-bark;

-- 
Jonathan "Dataweaver" Lang

Reply via email to