Pondering the Mop Up

So, I finally got around to reading the introduction that dams put together for p5-mop.  (If you don’t know what that is, it’s the effort to get a MOP, or “Meta-Object Protocol,” into the Perl 5 core.  This would not be adding Moose to Perl 5, but it would be adding something that would make Moose—or any competitors—a lot more feasible, efficient, and user-friendly.) I’ve been following Stevan’s blog posts about the ongoing work, but a lot of times those are more about certain implementation details.  They’re very useful, and very interesting, and I hope he continues to do them, but sometimes they can be a bit forest-for-the-trees, if you see what I mean.  I think it was great of dams to give us a 6,000 foot view, as it were.

Now, I haven’t had the opportunity to try out p5-mop myself yet, although I certainly hope to sometime soon.  So, as you read my thoughts about the MOP features that dams describes for us, remember that this is a more of a first-impression reaction and less of a practical response after use.  Still, I’ve been thinking about these sorts of things for many years now, so these are not off-the-cuff comments either.  It’s definitely a section-by-section reply to dams’ post, though, so make sure you’ve read that first.

Ready?  Excellent.  Here’s my impressions of the P5 MOP so far.

The first section is called Why is it important to try it out? And he’s spot on here.  I think (I believe quite strongly, in fact) that this will shape the direction of OOP in Perl for quite some time to come, even if it somehow never actually makes it into core (although I think it will).  And I think the direction of OOP in Perl will shape the overall direction of Perl, because I don’t think a modern language can surive without good OOP facilities.  Don’t get me wrong: I want Perl to continue to be the multi-paradigm language it’s always been, and I don’t want to see Perl programmers forced to write everything as an object whether they like it or not, like some langauges do.  But objects in Perl have to get easier if the language is going to continue to thrive.

Next section: Install it.  Excellent ... sounds very easy to do.  (Although I don’t particularly see a need to fork p5-mop-redux; I’ll just clone it read-only.)

Next, A first example.  Okay, and here’s my first hang-up with the direction: twigils.  I am not a fan of the concept.  Yes, yes, I know it’s a Perl 6 thing, and Perl 6 has had them for over a decade now, and they’re straight from Larry’s brain.  Which is an excellent brain, but I don’t have to agree with everything that comes out of it, right?  I never liked hijacking dot to use for method calls, and I never liked twigils (in fact, those are probably the only two things I don’t like about Perl 6).  I understand why they’re done that way, and I understand the advantages.  I still don’t like it.  Now, to a certain extent, this is just a personal preference thing, and I certainly don’t expect the future direction of Perl is always going to place my happiness above everything else.  You don’t always get what you want in life, and I have no doubt that I’m not going to get everything I want in Perl’s future OOP.  Besides, maybe they’ll grow on me after I actually start using them.

But my worry is that it’s going to be a barrier to entry.  Not for people to learn—I think that I’m probably an anomoly, and most of the people who hate twigils hate the whole OOP direction anyway and thus will never have to use them.  I’m talking about a barrier to p5-mop’s entry into core.  Will people feel uncomfortable about letting in something this weird, syntactically?  Hopefully I’m just overthinking it.

Other than that, I can’t see anything to object to here.  The change in meaning for is (to be closer to what Perl 6 uses it to mean) is fine by me.  The new way of doing defaults/builders is fine.  And of course, as a user of MooseX::Declare and Method::Signatures, the majority of the “new” keywords there are old hat to me.  Can’t say I’m fond of $self->next::method ... seems much clumsier than Moose’s super().  But that’s a pretty small pont.

Next up, we have Attributes traits, where dams goes more into how is will work.  Seems fine.

Next, Default value / builder.  As I said, there’s nothing wrong with this; in fact, it’s better than the current situation in Moose.  You don’t have to learn about the difference between defaults and builders and then constantly fret over which one’s better.  And you don’t have to worry about when you have to wrap your default value in a sub {} block and when you don’t need to.  Win win, I say.  I see some commenters don’t like the semantic overloading for =, but a certain amount of semantic overloading is inevitable in programming languages (look at how many things we use curly braces for!).

After a short blurb on Class and method traits (nothing to add there), we move on to Methods parameters.  As one of the authors of the Method::Signatures module, you can guess that I’m pretty stoked by this stuff.  Now, one commenter says,

Especially since users might want to choose a third-party framework like Method::Signatures for that, to have consistent behavior for both methods and functions...

Why hard-code a specific (and rather limited) method signature parsing/unpacking framework into p5-mop?

My view on this is exactly the same as on Peter Martini’s laudable effort to get method signatures into core.  Get something in the base language for people to use, then modules like ours will extend that for the people that want more features.  Yes, it means that schwern and I are going to end up having to reimplement the guts of Method::Signatures.  That’s fine.  I was already planning to do that anyway, to allow people using 5.14 and higher to be able to take advantage of the new keyword plugin API, since Devel::Declare is falling out of fashion these days.  So what’s one or two more back-ends?  It’ll probably end up making the module easier to extend in the long run anyway.

Next dams covers Types.  I’m a bit torn on this aspect.  What dams says is basically that it’s good that types won’t be going into the p5-mop effort, because this way you can choose whatever module you want to add that functionality.  Of course, by that logic, there’s no point to trying to get anything new into core, ever.  So I’m not sure I go along with that argument.  Still, I have to admit that it’s a logical piece to cut in order to keep the effort simple, and less likely to be rejected in the end.  If you’re making a movie out of The Lord of the Rings, the first thing you’re gonna cut is Tom Bombadil, and, if you’re putting a MOP into the Perl 5 core, the thing to go has gotta be types.  Still, I don’t have to like it.  dams says: “Sometimes you don’t want any type checking.” True, but in that case I just leave out the type.  What if I do want it?

After a brief mention of Clearer / predicate, which are pretty trivial, we move on to Undef versus not set, and here I think is where dams and I are going to diverge the most.  He says:

After all, we got this “not set” state only because objects are stored in HashRef, so it looks like it’s an implementation detail that made its way into becoming a concept on its own, which is rarely a good thing.

I disagree.  A language can, if it so chooses, distinguish between unset and set to nothing: bash does, and (apparently) so does PHP, and who knows how many others.  In fact, I would tend to look at this the other way around: because p5-mop chose not to implement their objects as values in a hash, it looks to me like losing this distinction is an implementation detail that we’re going to get stuck with.

Now, it’s true that an ordinary my variable can’t tell the difference.  In fact, Stevan replies in a comment to someone else who has the same objections I do:

... so in a sense we are returning the use of undef back to its natural perlish state, just as Larry intended.

But that’s looking at things a bit too conveniently.  While ordinary scalars can’t distinguish between unset and undefined, hash values certainly can—that’s the entire point of exists, and that’s a pretty natural Perlish state.  Values in an array sort of can, but the big one is that, despite what dams says, you certainly can distinguish between an argument not passed to a function vs one that’s passed as undef:

foo();
sub foo {
    my ($arg) = @_;
    if (@_ == 0) {
        say "arg not passed";
    }
    elsif (!defined @arg) {
        say "arg passed but not defined";
    }
    else {
        say "arg passed and defined";
    }
}
(One of the commenters called him out on this as well.) In fact, this is how the whole when clause in Method::Signatures got invented.  And sometimes that distinction is important.  Sure, sometimes you don’t care, but then you can just look at whether it’s defined or not and call it a day.  What if you do care?  Apparently, for class attributes in p5-mop, you just cry.

I haven’t quite decided how tragic this really is though.  I will definitely get boned by this at some point, but, if I’m honest, it’ll probably only happen once or twice, and I’ll probably work around it pretty quickly.

Then a brief comment on Roles (that all looks good), then another brief blurb on Meta.  On that, I can only answer answer dams’ question “how many time have you written make_immutable ?” by pointing out that I use MooseX::Declare, so: very very few, as it happens.  But, yes, classes should certainly be immutable by default.

As to Method Modifiers, I will have to agree with dams that $self->${^NEXT} is even worse than $self->next::method.  I don’t necessarily think that means that we have to have method modifiers in p5-mop though.  I think someone (probably someone in the Moose cabal like Stevan or Jesse) will probably write a module to simplify the syntax to what those of us using, say, MooseX::Declare along with Method::Signatures::Modifiers have come to love and expect.  I’m okay with that being the case, if it simplifies the MOP and therefore makes it more likely to be accepted.

Then dams comments on ${^NEXT} and ${^SELF} (if those are the alternative to twigils, I may have to rethink my position ...) and Twigils for public / private attributes (not any worse than twigils in general, I suppose).  Then he complains about is in why is ? we already have has ! Two verbs on one line of code, he points out.  Well, let’s see: The class “Point” has an attribute “x,” which is read-only.  Yep, that sentence has two verbs in it too.  So I’m not too concerned there.

In the final two sections, dams talks a bit about Exporter (which doesn’t worry me much), and Inside Out objects versus blessed structure objects, but hopefully I don’t have to care about implementation details like that.

So, overall, I’m moderately happy with the direction we’re headed in, and looking forward to the chance to try it out.  Perhaps I’ll take a couple of my classes I’ve written for personal projects and try converting them over and seeing how it goes.  Although I may have to hack up something for Method::Signatures so it generates p5-mop code.  But that’ll be fun.  (For certain values of “fun.”)

One thing I didn’t see anyone talk about yet is that a switch from package to class is going to break some infrastructure bits and bobs.  I’ve already run into this just from trying to use MooseX::Declare.  PAUSE doesn’t recognize it as a namespace, Dist::Zilla doesn’t recognize it as a module name, etc etc.  Occasionally I’ve wondered if this is going to be the real barrier to getting this style of syntax accepted in mainstream Perl culture.

7 Comments

Hi

Thoughtful comments. Thanx.

Now, as for deriving a class, how about:

$self -> evolve()

to indicate descent from an ancestor?

> because p5-mop chose not to implement their objects as values in a hash, it looks to me like losing this distinction is an implementation detail that we’re going to get stuck with

I can live with this:

- most attributes' types do not accept undef (so a value of undef will always unambiguously mean "not set"), and indeed it is considered a design failure to define a type constraint as Maybe[Foo];

- MooseX::UndefTolerant also combines "unset" with "undefined" by ensuring that passing (foo => undef) to a constructor will not blow up, but will simply not set that attribute.

I think it will have far less design ramifications than it would at first seem.

Buddy,

Thanks for the in depth comments, this is great! Couple of things I wanted to note.

1) Twigils really do grow on you, I agree they are a little offensive to the eyes, but they really help make attributes stand out in a sea of otherwise normal variables. And yes, we realize that is our biggest barrier to entry into core, so who knows, they might get rejected :)

2) ${^SELF} has been removed (and ${^NEXT} was just a quick hack for the method modifiers, so likely won't stay for ever either).

- Stevan

Leave a comment

About Buddy Burden

user-pic 14 years in California, 25 years in Perl, 34 years in computers, 55 years in bare feet.