Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

OO in Perl 5: still inadequate

by Aristotle (Chancellor)
on Jan 20, 2006 at 21:00 UTC ( [id://524574]=perlmeditation: print w/replies, xml ) Need Help??

Update: don’t miss xdg’s reply, which addresses the issue.

This is an off-the-cuff note that started out as a reply to xdg in Re^2: Slides from NY Inside-Out Talk, where he writes:

I’m pretty indifferent, personally, on advisory versus enforced encapsulation.

I’m not: advisory is better. Being unable to do nasty things necessarily means you also cannot do powerful things. The primary value brought to the table by inside-out objects for me is that subclasses cannot accidentally break encapsulation. I would prefer if they could, iff they so chose; then the OO in Perl 5 would be on par with Ruby.

So far, the choices are:

  1. Encapsulation is advisory and accidentally breakable; subclasses require implementation knowledge of the superclass. (Hash-based and co.)
  2. Encapsulation is enforced; subclasses require implementation knowledge of the superclass. (Closure-based.)
  3. Encapsulation is enforced; subclasses require no implementation knowledge of the superclass. (Inside-out.)

Notably absent, however:

  1. Encapsulation is advisory; subclasses require no implementation knowledge of the superclass.

Makeshifts last the longest.

Replies are listed 'Best First'.
Re: OO in Perl 5: still inadequate
by xdg (Monsignor) on Jan 21, 2006 at 06:33 UTC
    4. Encapsulation is advisory; subclasses require no implementation knowledge of the superclass.

    Actually, that's easy. Just use package variables instead of lexical variables.

    package My::Class; use Scalar::Util qw( refaddr ); # package hash for inside-out properties, instead of lexical our %name; sub new { my $class = shift; bless {}, $class; } sub name { my $self = shift; if (@_) { $name{ refaddr $self } = shift } return $name{ refaddr $self }; }

    I did a variation on that for my highly experimental Object::LocalVars and called it "outside-in".

    Actually, while I haven't tested it, there's probably no reason it couldn't be done today with thread safety and all the rest with Class::InsideOut -- it only needs a hash and it doesn't check to see if it's a package hash or a lexical hash. You'll get thread safety, Storable support, etc.

    Equivalent to the example above, but robust:

    package My::Class; use Class::InsideOut qw( public register ); public name => our %name; sub new { my $class = shift; register( bless( {}, $class ) ); }

    P.S. I did a quick test changing a test-suite property to our instead of my and the test suite ran fine, so I suspect the outside-in variation works fine with Class::InsideOut.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      Uhm.

      Wow.

      All I can say is, d’oh. I don’t know why something so obvious never occured to me; sometimes I can’t see the forest for the trees.

      Cool! Thanks for pointing this out.

      Makeshifts last the longest.

        I don’t know why something so obvious never occured to me

        Don't worry about it. <grin> It happens to all of us at some time or another. Inside-out objects are unusual enough still that it takes a little more effort to pick apart the different angles on it.

        In my presentation, I open by stating there are three fundamental concepts for people to take away:

        1. Objects as containers versus objects as indices

        2. Encapsulation via lexical closure

        3. Memory addresses as unique identifiers

        Everything else is just combinations and variations of these three ideas.

        -xdg

        Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: OO in Perl 5: still inadequate
by brian_d_foy (Abbot) on Jan 20, 2006 at 22:28 UTC

    Perl 5 would be on par with Ruby in which respect? You can't use phrases like "on par" and "inadequate" without also explaining your metric.

    I find that these sorts of arguments are really about something else. On the one side some people want perfect code that fits into some idea of beauty or mathematical purity in their head and they spend inordinate amounts of time trying to get there. They get uncomfortable unless everyone programs like they do. The other side wants to get work done, and they stop when they finish their task. Pretty code might not even be on the list of things they need to accomplish before they go home at the end of the day. On yet another side, non-technical people know they need software but they don't know how to judge the quality of a programmer or they make their choice based on price. They trust their software to somehow who claims to be able to program (and certification in some language isn't going to show who can program well and who can't). Those people care about getting something they can use and that has value in the future. They want code that some other programmer can modify. Lately, I've been dealing with programmers who just want to program and don't care about any of the other stuff as long as they can continue to get people to give them money to subsidize their computer hobby.

    So, who should win? Everyone has a different answer and that's why there's more than one programming language. Once you choose what you care about more, then we can talk about a particular language's fit for that. For me, Perl is completely adequate for getting work done and adding value to the world. Ruby is not adequate despite being nice language. It needs a CRAN.

    Object-oriented programming isn't a language. It's a way of doing things. That Perl, Ruby, Java, whatever do it differently doesn't matter that much. Even if you fixed this niggling thing in Perl so it matched some other language, that isn't going to stop people from writing bad code. In my experience, no level of language enforcement stops bugs. Those bugs will just break out in other places. Just wait to the dumb kids start using Ruby to see a lot of bad Ruby code.

    I don't mind that Perl allows people to break encapsulation. It's a lot like the guy walking into the doctor's office and saying "I bleed with I poke myself with this knife. What should I do?". Well, you don't poke yourself with knives. Another person might ask "What if someone pokes me with their knife?" In that case, stay away from people who poke other people with knives.

    That some people can do bad things doesn't convince me that I should give up using Data::Dumper on an object without creating an as_dumped_string method in every class.

    If we want to complain about Perl's shortcomings as an object oreinted programing language, let's talk about the lack of objects (like I do in Use Perl 6 Now in this month's TPJ ;).

    --
    brian d foy <brian@stonehenge.com>
    Subscribe to The Perl Review
      That some people can do bad things doesn't convince me that I should give up using Data::Dumper on an object without creating an as_dumped_string method in every class.

      That's a slightly tangential argument. Using Data::Dumper on an object implies an assumption that an object is its own data structure. That breaks down when the object is merely an index into external data structures, irrespective of whether those external data structures themselves can be dumped.

      It's really a paradigm switch from OO programming (objects have behaviors) to imperative programming (functions have arguments) and that only works so long as there's a common practice to write objects that work both ways.

      At least Storable has a (somewhat) sensible way of allowing objects to play nice with it. Data::Dumper does not, as I found out in Hooks like Storable for Dumper?.

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

        It's a completely tangential argument, because I think people argue about the wrong things. :)

        When people start talking about what Perl should do (or shouldn't do), they forget to think about what they have to give up. Seemingly simple changes have long-reaching consequences, and there are trade-offs. You never just fix one thing. You fix it and break other things. In this case, when you fix the encapsulation issue, you break my whipupitude.

        In Perl I don't have to think about your paradigm shift because I don't have to choose. I don't even have to shift. I use what's expedient. That I can mix these in Perl is quite powerful. It works because there already is a common practive to write things both ways. That's always going to work in Perl because an object is just a blessed reference, and most objects I run into are going to be anonymous hashes, despite all the odd things that people could do with their classes.

        --
        brian d foy <brian@stonehenge.com>
        Subscribe to The Perl Review

      You can’t use phrases like “on par” and “inadequate” without also explaining your metric.

      I did lay out the metric; that’s what the post was about.

      So, who should win?

      What I am after will offend some people’s sense of beauty, but works out favourably on all other metrics, near as I can tell. Which is just why I want it.

      In my experience, no level of language enforcement stops bugs.

      I’m not looking for enforcement – I said as much in the post, right? I want encapsulation to be the default situation, but not the only one. I want to be able to write a subclass without worrying about what any superclass is doing under the covers, because I’m secure in the knowledge that if I don’t go rooting around for anyone’s underwear, I won’t run into it. It’s the same principle as global variables.

      Just wait to the dumb kids start using Ruby to see a lot of bad Ruby code.

      This the exact point I made in an article on lesscode.org.

      I don’t mind that Perl allows people to break encapsulation.

      I would mind if it didn’t. (Ruby allows it too, for that matter.)

      That some people can do bad things doesn’t convince me that I should give up using Data::Dumper on an object without creating an as_dumped_string method in every class.

      Precisely. And more to the point, being able to break encapsulation allows abstractions that are impossible in a language with enforced encapsulation. And that, I really care about. A tool with which you can’t do any damage is worthless.

      I’m not asking for strong encapsulation, I am after an OO approach where isolation is the default, so I won’t have to worry about accidentally breaking superclass code, just because I decided to use a private instance variable that happens to be named the same as a private instance variable in one of the superclasses.

      Hence “advisory encapsulation.”

      Makeshifts last the longest.

        By metric, you have to explain how you measure adequacy. You haven't done that. If you meanto imply that adequacy is completely measured by encapsulation then the argument is quite silly.

        I'm not arguing with you about encapsulation. I think you missed the point about OO programming by arguing that you can't do it adequately in Perl.

        --
        brian d foy <brian@stonehenge.com>
        Subscribe to The Perl Review
Re: OO in Perl 5: still inadequate
by fergal (Chaplain) on Jan 21, 2006 at 00:59 UTC
    Why is hashed-based #1 and not #4? What implementation knowledge do I need to subclass a well-written "standard" hash-based Perl class?

      #1, that it uses blessed hashes. #2, which keys you cannot use for your own purposes in the hash. #3, if it uses accessors.

        I don't understand #3, accessors are what allow ignorance of super classes but you can always look inside a hash accessor and bypass it if you want. #2 I'll deal with in a more general argument in a minute and #1 is included in my "bold claim".

        Here's my bold claim: any class is subclassable without "knowledge" of it's implementation.

        I'll explain the quotes around "knowledge" in a minute.

        All that's required is an inside-out class builder that keys off something like the refaddr of the object and doesn't really care what kind of blessed refs. I'm pretty sure such builders exist, although I can't name one (I can't name any i-o object builder :). If they don't I don't see any great obstacle to building one. Voila - subclassing without knowledge of the superclass's implementation. These classes are themselves subclassable in the same way. All attribute manipulation would be done via accessor methods generated by the builder class, that way encapsulation is advisroy not mandatory.

        So assuming you agree that the above scheme exists or almost exixts, it's time to address "knowledge". Does the above need to know the type of the blessed reference? No. Does it need to know the contents? No. How about the names of any methods? In general, no, however we have to avoid trampling over any undocumented methods in the superclass because they could be called by a method in the superclass and do something completely unexpected.

        These undocumented methods could be a problem but consider the requirement for advisory encapsulation. What this means is that undocumented methods must remain accessible and so it's impossible to avoid this problem. You either want them to be accessble or you don't, you can't have both (this answers #2 above, although for hash based objects, using __PACKAGE__."keyname" for keys also helps a lot).

        So I think it's quite straight forward to achieve both goals for this definition of "knowledge". You cannot{1} avoid having some knowledge if you want to retain advisory encapsulation.

        Example: class Shape uses blessed glob refs. We want to sublcass it for Rectangle which we do, adding 4 undocumented accessors for _Point1, _Point2, Point3, _Point4 and 1 documented Corners. The values for these are stored inside-out and are looked up based on the refaddr of the object. No knowledge of Shape was required apart from that we mustn't add any methods that override Shape's undocumented methods. We can in turn subclass this but we shouldn't override it's undocumented accessors _Point{1-4}.

        Basically if you want advisory encap then can't have total ignorance of the superclass. Given that, the rest is easy.

        {1} Well actually you almost can but it involves wrapping all methods in something that checks the caller to see if the call is coming from inside the superclass in which case we dispatch to the old method, otherwise the new but that's exceedingly ugly and slow and still isn't 100% reliable (think a callback doen from inside the superclass that ends up calling one of these methods, do we give it the inner or outer version?).

Re: OO in Perl 5: still inadequate
by Anonymous Monk on Jan 20, 2006 at 22:35 UTC
    Ruby, like Perl, still allows you to redefine your classes at runtime: that alone was enough to ruin the language for me. At least perl warns you when you redefine a function; I got no such warnings from Ruby.

    I’m not: advisory is better. Being unable to do nasty things necessarily means you also cannot do powerful things.

    Powerful things tend to devolve into nasty things in short order, IMHO. I'm a fan(atic?) of the KISS principle; I'd rather see something be simple, consistant and clunky than powerful, elegant, and full of unexpected pitfalls due to excess cleverness. Don't do something unexpected is my motto: if you have no other recourse, document the hell out of why and how you came to that decision, and what the implications that decision was.

    I probably don't live up to my aspirations, but I shun "powerful" code for that reason: it's usually only powerful because it's not very easy to understand, because it does too much at once. Or maybe I'm just too dull to deal with it. *shrug*
    --
    Ytrew

      Ruby, like Perl, still allows you to redefine your classes at runtime: that alone was enough to ruin the language for me. At least perl warns you when you redefine a function; I got no such warnings from Ruby.

      Erm, did you have warnings enbled?

      gamera:~ 835> cat boink.rb class Foo def bar( ) puts "bar" end end class Foo def bar( ) puts "baz" end end gamera:~ 836> ruby -w boink.rb boink.rb:8: warning: method redefined; discarding old bar
        Apparently not; I was using a tutorial that was redefining classes as it went.

        Apologies to Ruby for the undeserved criticism. But is there some way to freeze the class definition so it can't be changed at all?

        I dislike giving other people plenty of rope to hang me with.

      My perspective:

      • The number of bugs is linear with the amount of code. Powerful constructs that reduce the amount of code you need to write therefore reduce the number of bugs in your code.
      • Powerful features can be wrapped in nice generalised packages for everyday use. There’s a difference between being Damian and being a user of Damian’s modules.

      A lot of the stuff in Class::* would be impossible, were it not for breakable encapsulation.

      Makeshifts last the longest.

        The number of bugs is linear with the amount of code. Powerful constructs that reduce the amount of code you need to write therefore reduce the number of bugs in your code. And shuffle it inside the constructs themselves, which are harder to debug, since they're powerful and complicated. You still don't win; you end up maintaining ugly libraries full of someone else's notion of "cool" or "powerful" constructs, and now the userspace problem has been halfway abstracted into a less-comprehensible language, in a half-tested toolkit.

        I've been down that road. I don't like it. It's made me suspicious of the whole "powerful code is good code", because usually someone's idea of "power" really is "ugly, complicated code that tries for too much at once". Sometimes it's not, and those people do good work; but it's the exception, not the rule.

        Powerful features can be wrapped in nice generalised packages for everyday use. There’s a difference between being Damian and being a user of Damian’s modules.

        Not really -- if you end up having to debug one of Damian's modules to fix some obscure bug in the middle of the night, you still have to know what the hell he did and how he did it. And then you'll probably wish he was less clever and used fewer obscure tricks, so that you could see the bug and FIX it, and go HOME, instead of fretting over how the classes and the closures and self-rewriting language constructs are conflicting in some wierd and unpredicted way.

        If you gamble on complex, powerful code you'ld better be sure you get it right the first time, because maintanance sure is a nightmare when the call to do_deep_magic() fails.

        And as the guy who has to fix stuff, I get grumbly when it's me that has to fix it. I don't like beautiful and elegant code. Sure, maybe you're a genius with knots, and you can make a sailor jealous with your bowlines. And maybe you think it's a conceptualy elegant and powerful notion to solve the problem of cable management with the cables themselves. But to me, it's still a mistake to make your network cables look like a some elaborate piece of celtic knotwork that's an absolute pain to untangle at 3:00 am when the same job can be accomplished in an inelegant, ugly, but obvious way using cable ties and conduits that can be fixed on a moment's notice late at night.

        I've seen so many "code as art" projects that were an abject nightmare to deal with not to scream internally every time someone starts chuffing their chest, and touting words like "elegance", or "powerful constructs", or "higher order" anything.

        Maybe I'm just a grump. But I've seen a lot of stuff like that, and I keep having to clean up after young, bright eyed kids who think they know what they're doing, but can't quite nail it -- they bustle in on contract, fire buzzwords around, generate a lot of cute, complicated code, get bored, leave before they can be fired ( or get fired), and leave me to clean up the mess.

        That's no fun.

      I think your definitions are either overloaded, or perhaps even altogether broken. Simplicity and power are not mutually exclusive, yet you seem to be saying they are. I actually tend to find exactly the opposite -- the simplest things are very often the most powerful. (As a concrete example, consider a unix pipeline. Hooking the output from one program to the input from another is extremely simple, yet allows for amazingly powerful constructs.) I guess it might just be a matter of definitions, but I have to take issue with yours.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://524574]
Approved by Fletch
Front-paged by BrowserUk
help
Chatterbox?
and the web crawler heard nothing...

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

    No recent polls found