in reply to Re^3: Agile syntax (abuse?)
in thread Agile syntax (abuse?)

This relies on the possibility to replace the body of an inside-out object, (normally an undefined scalar) with some foreign object of any provenience, (also a scalar in this case, but with significant content) so that its methods work without a hitch. This approach won't work with a hybrid class where the body is required to be code.

(++) x As::Much->as('possible'); (which unfortunately is only 1) both for the intrinsic educational value of your post and for the nifty example you came up with. Perhaps all this may be the basis for a future tutorial... OTOH I must admit I'm such a blockhead that while I could understand at a glance why all this worked and grasped the beauty of the scheme, initially I could not see what would have gone wrong with "my" implementation, but eventually I did.

Said this, I know I could find out by experimenting myself and reading the docs, but I seem to notice that "this kinda things" tends to present more possibile gotchas waiting in the shadow to bite you in the neck than one on average would expect, so I'm asking directly: if the implementation is a pure Inside-Out ref-agnostic one and deref overloading is used as in the above, can one reliably use dereferencing "as a shortcut" (supposing that in a realistic case that would be part of a documented public interface) in the derived class implementation?

Replies are listed 'Best First'.
Re^5: Agile syntax (abuse?)
by Anno (Deacon) on Apr 05, 2007 at 18:24 UTC
    ...educational value of your post

    I'm doing this as much for my own edification. I am glad for the opportunity to discuss general principles in the context of a concrete class implementation. If it hadn't been for the context of, specifically, 2x2 matrices I wouldn't have come up with the example of combining a matrix class and an angle class to form a class of rotational matrices. I've been looking for an example that makes some sense of using two classes in tandem, more than attaching a label or a time stamp to anything. I am glad the discussion brought one up.

    ...could not see what would have gone wrong with "my" implementation

    One property of true inside-out classes is that you can supplant their (undefined-scalar) body with any other reference without impeding their function. This is how inside-out classes are able to make one conventional class a base class, by adopting its body. The result is a hybrid class that is no longer able to inherit from another conventional class without the problems of normal Perl inheritance. Your implementation was hybrid to begin with, so it lost the ability to inherit from a conventional class.

    In general, an object can be initialized to any number of inside-out classes, plus one conventional class which determines its body. None of these must be the class the cobject is blessed into.

    To your question: If the implementation is a pure Inside-Out ref-agnostic one and deref overloading is used as in the above, can one reliably use dereferencing "as a shortcut" (supposing that in a realistic case that would be part of a documented public interface) in the derived class implementation?

    I don't quite see why you'd call it a "shortcut", but yes, it should work. That doesn't mean it is generally a good idea.

    Overloading (any overloading, not just references) is a liability to inheriting classes -- you don't get rid of it easily and it's a nasty surprize if a minor contributing ancestor overloads, say, "bool" in unexpected ways. If at all possible, overloading should be optional. That would mean it can't be used in the class implementation itself.

    If overloading is an intrinsic part of the class, there is no reason (except, perhaps, speed considerations) not to use it in the class implementation.

    As to ref overloading specifically, there is always a conflict when you overload the type of reference that the object body actually is. For pure inside-out classes this is irrelevant, but it matters for their potential hybrid subclasses. In other words, if you decide that your class overloads hash dereferencing, and a conventional hash class wants to make you a base class, there's a conflict. The methods of the hash class won't be able to access the body. Similarly there would have been a conflict in my example if the Angle class had happened to be implemented as a class of coderefs.

    The conflict can be avoided by making the overload routine caller-sensitive so that it returns its argument unchanged to all code that isn't compiled in its class. In Matrix.pm, the routine for overloading "&{}"

    sub _code : lvalue { $code{ refaddr shift} }
    should have been
    sub _code : lvalue { my $obj = shift; return $obj unless caller eq __PACKAGE__; $code{ refaddr $obj}; }
    It is easy to forget this little courtesy to potential subclasses. Like you said, the area of overloading, and ref overloading in particular, has more possibile gotchas waiting in the shadow than is immediately apparent. Inside-out techniques add new aspects to the scenario.

    Anno