in reply to Re^2: Solving the SUPER problem in Mixins with a Dispatch Method
in thread Solving the SUPER problem in Mixins with a Dispatch Method

Seems to me that method namespaces would be overkill for a problem that could be solved in one of several other ways.

First, 'shoot' is a badly chosen method name for either the GunSlinger or SoccerFootballPlayer Mixin (hehe). The same problem would occur in real life (sic) if the gun-toting centre forward happened to be dribbling the ball into the enemies 18 yard box when the opposing goalie went for his deringer. Someone shouts "Shoot!". What does he do?

Yes, it's a fairly unrealistic scenario, but then so is the program that requires the Person class to utilise both Gunslinger and SoccerPlayer mixins concurrently.

It's like the 'diamond inheritance' problem described elsewhere, it not too hard to dream up simplistic examples of such problems, but I wonder how often they actually occur in real code?

I am not a fan of MI either, but the problems I've encountered with it almost all stem from the complexity of designing Classes and Class hierarchies with MI. And the main one of those problems is that MI prevents you from getting on with the job at hand.

Rather than being able to concentrate on designing a Class to do a particular function, you are forever scrabbling around the existing class hierarchy trying to find the best place to slot it in; decide how to best leverage existing code; attempting to re-use every vaguely similar piece of code in the library; and trying to construct the class in such a way that the next time someone wants to do something similar, they will be able to inherit as much common functionality from this class as possible.

The upshot of this process is, that gazillions of hooks, stubs and extra layers are added in just incase. And experience shows that they rarely get used.

MI just puts the design process into quagmire of terminal inter-dependancies.

Even the most thoroughly well thought through Class hierarchies needs wholesale refactoring the moment someone comes up with a new requirement.

The namespace clash problem with Mixins can most simply be solved by using less generic (more specific) naming conventions for method names. $person->shootBall; and $person->shootGun;, but rather than worrying incessantly about such problems at the design stage, it's probably expedient to wait until such conflicts actually arise.

One possible solution to the potential problem, would be to have syntax available to allow Mixin method names to be aliased by calling(?) classes. Eg. Something like:

my $person = new Person() is Footballer( shoot as shootBall ), is GunSlinger( shoot as shootGun ) ;

I find it hard to imagine the scenario where a gun-toting footballer would need to generically dispatch to the 'shoot' method according to which roles he was playing at the time. It's much more likely that any code needing to call either the shootBall or shootGun methods is going to be explicitly coded.

Whether this would hold true for (all) real code examples is an open question.


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

Replies are listed 'Best First'.
Re^4: Solving the SUPER problem in Mixins with a Dispatch Method
by fergal (Chaplain) on Oct 14, 2004 at 16:21 UTC

    The problem with choosing the right names is that it requires some sort of psychic ability to foresee the clashes that might happen. It also leads to long method names even when there is no danger of ambiguity. For example inside the method Footballer::TakePenalty we shouldn't have to say shootBall because no other form of shooting makes sense in this context.

    Namespaces are an inherently more powerful and general way of disambiguating and allow solutions that are simply not possible when you just make longer and longer names instead.

    That is why we have packages and functions like Data::Dumper::Dumper() not data_dumper_dumper()

    Without namespaces, calling a method is like asking for an action where the implementation and the entire meaning of the action is left to the object - so basically you are just hoping that you and the object both have the same vocabulary. With namespaces, only the implementation is left up to the object, the meaning of the method call is open to interpretation.

    So for example, assuming that all the format() methods have been implemented correctly, not using namespaces means that you could end up with a formatted disk when you really wanted a formatted string. If namespaces are available then there can never be any such nasty accidents. A more real example of the dangers of allowing the object to supply not just the implementation but the meaning.

    use IO::All; my $file = io("somefile"); log(5, $file, "some error"); sub log { my ($file, $level, $message) = @_; $file->print($message) if $level < $DEBUG_LEVEL; }
    Getting the arguments swapped causes us to silently record rubbish into our log because IO::All changes not just the implementation but the entire meaning of <.

    I think the reason this hasn't been implemented yet it because we get quite a bit of disambiguation by looking at the object on which the method is called. However sometimes that's not enough - mixins being a prime example.

    Aliasing is a horrible hack and breaks genericity - if the method no longer has the correct name then you cannot pass that object into a generic function and expect it work.

    As for unreal examples, yes, they're easy to create but real examples to exist. Where I really missed MI recently was when implementing a DB layer in Object Pascal. I needed to implement objects which were essentially cursors and allowed you access to the fields of the current row. I had to implement 3 types, one for ADO, one talking directly to ODBC and another talking to SQLite. So I inherited various bits from a base class and had 3 specialised subclasses, one for each target. The problem is that there are various behaviours of cursors that are orthogonal. They can be forward-only or random access cursors, updateable or read only. Mixins would have allowed me to bolt on these behaviours to the existing classes. I had to use delegation for the updateable vs readonly and I ended up having to write lots of boilerplate delegation methods. For the forward only situation I ended up putting the shared code into the base class and so there was no distinction at a class level between forward only and random access. This meant that I had to wait until run time to catch people trying to go backwards with a forward only cursor. If I had made them separate classes, these errors would have been caught at compile time but using delegation to do that was too fiddly to be worth it.

    Delegation causes code like this

    package Thingy; sub method { my $self = shift; $self->del->method($self, @_); } pakage Delegate; sub method { my $self = shift; my $parent = shift; # in here we completely ignore $self and do # lots of manipulation of $parent }
    which is just plain wrong. Even if you can autogenerate the first method the second method should make you exceedngly worried because it completely ignores $self and spends all it's time poking around inside $parent. It is crying out to be a method in Thingy rather than in Delegate.
    The upshot of this process is, that gazillions of hooks, stubs and extra layers are added in just incase. And experience shows that they rarely get used.

    I don't think this is anything to do with delegation or mixins, this is simply trying to stuff too much into a single class and can happen by either method.

    Can you point out anything specific that is dangerous or unworkable when using mixins along with namespaced-methods?

    We are getting mixins in Perl 6 (via something like traits). I think the apocalypse, the exegesis (if it's out) and that paper will demonstrate their usefulness. Unfortunately I think we are getting at some of their bad points too.