OO in Perl 5: still inadequate
Aristotle
created: 2006-01-20 16:00:54

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 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.

Re: OO in Perl 5: still inadequate
created: 2006-01-20 17:28:23

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
Subscribe to The Perl Review
Re^2: OO in Perl 5: still inadequate
xdg
created: 2006-01-21 08:07:32
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.

Re^3: OO in Perl 5: still inadequate
created: 2006-01-21 12:17:19

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
Subscribe to The Perl Review
Re^4: OO in Perl 5: still inadequate
xdg
created: 2006-01-21 14:30:19
most objects I run into are going to be anonymous hashes

That's a convention and it works only so long as people follow the convention. Conventions can change, unlike (or at least faster than) features built into the language.

In this case, when you fix the encapsulation issue, you break my whipupitude.

That's true, but so what? The "whipupitude" (?) was broken to begin with because it assumed that people would only write objects one way. Adding encapsulation just reveals that flaw and, ideally, would prompt some better thinking about "whipupping" in a more diverse way.

I go back to Storable as an example of a module that lets people work in either paradigm and is humble enough not to assume that things will always be done one way. Unfortunately, its way of doing that means implementing a custom hook.

We actually could use a more standard convention in Perl for a transparent structure for objects and built-in types. It would need to be complimented with a standard method name for generating/processing them to be useful in multiple places. That's where a "built-in" feature of the language could help create some standardization.

For example, just as we have stringification, numification, etc., via overloading, perhaps we need "objectification" to a standard format as well.

-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^2: OO in Perl 5: still inadequate
created: 2006-01-21 10:27:41

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’s 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 asdumpedstring 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.

Re^3: OO in Perl 5: still inadequate
created: 2006-01-21 12:01:04

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
Subscribe to The Perl Review
Re^4: OO in Perl 5: still inadequate
created: 2006-01-21 12:16:02

The metric is: is there a way which keeps my subclass a) isolated and encapsulated from any implementation details of the superclass b) while later letting me break this subclasses’ encapsulation if I so choose? I explained how the approaches known so far fail to satisfy this metric. (Except I missed the fact that xdg pointed out.)

Ruby offers that, which is why I said Perl 5 is not on par with it. (Turns out it is, though.)

Makeshifts last the longest.

Re^5: OO in Perl 5: still inadequate
created: 2006-01-21 12:28:12

That's the single measurement that makes Perl 5 OO inadequate? Nothing else matters? That's the only thing you are going to use when you compare other languages?

Perhaps you should have chosen a better title for your node: "Perl OO encapsulation inadequate", because that's all you're talking about and its a very poor judge of a language's adequacy. Your claims that Perl is deficient come without anything Perl doesn't allow us to do or a way in which it fails to help us get work done.

I think, then, that I'd have to put your argument in the category of "mathematical purity wanking" because you haven't shown any inadequacy in any other dimensions I explained in my original reply. In everyday life, this issue is something far down on the list of things people care about.

As I said earlier, there are better things to argue about if you want to call Perl's OO inadequate. A better start is Perl's lack of objects. :)

--
brian d foy
Subscribe to The Perl Review
Re^6: OO in Perl 5: still inadequate
created: 2006-01-21 13:51:52

Sigh. First off, I noted it as an off-the-cuff note, something that chafes me about doing OO in Perl (yes, always). Would you argue that Perl without the strict strict would be… okay, let’s call it “not as adequate as Perl is now“? Would you disput that a Perl without package globals would be “less adequate”?

That’s all I want: strict for Perl 5 OOP.

Sure, I get things done without it. Inadequate doesn’t mean useless. I mean, people wrote a lot of useful code in Perl 4 too, yet Perl 5 is undeniably a better language than Perl 4.

Mathematical purity wanking? Well, if that’s what you want to call it, be my guest; I shall be a wanker.

Makeshifts last the longest.

Re: OO in Perl 5: still inadequate
created: 2006-01-20 17:35:37
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

Re^2: OO in Perl 5: still inadequate
created: 2006-01-20 23:56:26

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.

Re^2: OO in Perl 5: still inadequate
created: 2006-01-21 02:17:01
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                       
Re^3: OO in Perl 5: still inadequate
created: 2006-01-23 15:04:26
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.

Re^4: OO in Perl 5: still inadequate
created: 2006-01-23 16:03:20

Yup.

---------------------------------------------------------- Module#freeze
     mod.freeze
------------------------------------------------------------------------
     Prevents further modifications to _mod_.

And there's also Object#freeze which prevents modification of individual instances (preventing you from adding singleton methods to them).

Re^2: OO in Perl 5: still inadequate
created: 2006-01-21 11:08:11

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.

Makeshifts last the longest.

Re^3: OO in Perl 5: still inadequate
created: 2006-01-23 16:37:14
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.

Re^4: OO in Perl 5: still inadequate
created: 2006-01-24 07:43:14

It’s a question of whether you want to spend 3 days writing something that you could have gotten done in an hour using a Damian module. Sometimes, having magic available makes the difference from “too much effort to implement at all.”

And personally, I don’t take the notion of magic lightly. I rarely ever implement the first thing that comes to mind either (in this, I seem to be in a minority). I wouldn’t use Damian magic without a good reason for it anyway. And would rather not use Ingy magic at all. I’d never use a source filter in production. And on and on.

I know to discipline myself. I resent it deeply when the language assumes I don’t and puts itself over my judgement.

Makeshifts last the longest.

Re^5: OO in Perl 5: still inadequate
created: 2006-01-24 10:55:06
I know to discipline myself. I resent it deeply when the language assumes I don’t and puts itself over my judgement.

In your case, I believe you. That's not the common case, though. Most people who say they know how to discipline themselves just don't; and then other developers suffer for their failings. Self-discipline is hard.

It's like the way 90% of drivers consider themselves "above average" -- they can't all be right, and only a few of them are really as good as they claim.

A good programmer can work within a language to get whatever he needs done, one way or another. It's a question of limiting the amount of damage a bad developer can do (deliberately or by accident) that's hard.

Re: OO in Perl 5: still inadequate
created: 2006-01-20 19:59:16
Why is hashed-based #1 and not #4? What implementation knowledge do I need to subclass a well-written "standard" hash-based Perl class?
Re^2: OO in Perl 5: still inadequate
created: 2006-01-20 20:25:50

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

Re^3: OO in Perl 5: still inadequate
created: 2006-01-20 22:15:27

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^4: OO in Perl 5: still inadequate
created: 2006-01-20 23:51:09
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...

You can do this with Class::MakeMethods. (Cf. Class::MakeMethods::Template::InsideOut.)

Re^4: OO in Perl 5: still inadequate
created: 2006-01-21 01:30:26

Not every class is subclassable (although it would be nice if they were), knowledgable or otherwise. Some people do odd things like blessing an object into a hard-coded class (so you don't get to decide which class the object ends up in), have some factory-like behavior going on where the object ends up in a particular class, and lots of other things. Lots of code I see doesn't pass the null-subclass test.

If you're doing weird wrapper or adapter things, you really aren't subclassing.

--
brian d foy
Subscribe to The Perl Review
Re^5: OO in Perl 5: still inadequate
created: 2006-01-21 09:15:47

Yes. There's an implicit requirement on the classes we're discussing to be subclassable in the first place, otherwise the discussion is pointless. We may as be talking about the number of vertices in polygons and allowing polygons to include circles.

s/\bclass\b/class (which must be subclassable to start with)/g
Re^6: OO in Perl 5: still inadequate
created: 2006-01-21 12:30:26

If you limit the set of classes your claim applies to, you can't use the word "any". Your bold claim is too bold :)

--
brian d foy
Subscribe to The Perl Review
Re^4: OO in Perl 5: still inadequate
created: 2006-01-21 10:48:52

The point was that inside-out objects done using lexical variables are themselves strongly encapsulated. So you can subclass any class without knowing the implementation of the superclass, but your encapsulation is no longer advisory.

Well, until xdg made his “err, package vars anyone?” point further down in the thread and I headdesked.

Makeshifts last the longest.

Re: OO in Perl 5: still inadequate
xdg
created: 2006-01-21 01:33:03
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 [mod://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 [mod://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.

Re^2: OO in Perl 5: still inadequate
created: 2006-01-21 10:37:27

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.

Re^3: OO in Perl 5: still inadequate
xdg
created: 2006-01-21 11:08:47
I don’t know why something so obvious never occured to me

Don't worry about it. 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.

perlmonks.org content © perlmonks.org and Anonymous Monk, Aristotle, brian_d_foy, chromatic, fergal, Fletch, simonm, xdg

prlmnks.org © 2006 edmund von der burg (eccles & toad)

v 0.03