Things I Can't Do In Perl :(

Ovid on 2008-02-04T19:03:38

Update: Has anyone else been getting a lot of "can't connect" or similar errors. Lately I've been getting them constantly here, regardless of whether I'm posting from home or work.

I'd love to have a proper IDE. So tell me, how I figure out all of the method names in a class? Well, many are built up in BEGIN blocks:

BEGIN {
    no strict 'refs';
    foreach my $token (qw( plan comment test bailout version unknown yaml )) {
        my $method = "is_$token";
        *$method = sub { return $token eq shift->type };
    }
}

Or at runtime:

package Employee;
use base qw(Class::Accessor);
Employee->mk_accessors(qw(name role salary));

... via Moose:

package Point;
use Moose;

has 'x' => (is => 'rw', isa => 'Int');
has 'y' => (is => 'rw', isa => 'Int');

... maybe Spiffy:

package Keen;
use Spiffy -Base;
field 'mirth';
const mood => ':-)';
    
sub happy {
    if ($self->mood eq ':-(') {
        $self->mirth(-1);
        print "Cheer up!";
    }
    super;
}

Or via AUTOLOAD, defined in XS files, etc.

So how do I figure out those methods short of running the code? I can't. Hell, even if I could, this could easily trip me up:

package Foo;
use HTML::Entities 'encode_entities';

# later
if ( Foo->can('encode_entities') ) { ... } # danger!

I could check to see if the method was defined in the class hierarchy:

use Sub::Information;
if ( $class->isa( inspect( $class->can($method) )->package ) ) { ... }

But that's a pain and breaks in all sorts of annoying scenarios. But maybe it really does mean that this is the method I want? Well, it can't tell me that.

if ( $object->can('name') ) {
    $object->name($new_name);
}

Well, does that really take an argument, instead of &get_name? And is is really a void method?

These things are frustrating to me because what I would really love to do with Bermuda is include deserialization. I'm not sure how to do that. Perl is too random and has no real introspective capabilities. Perl 6 solves a lot of this, but I'm writing Perl 5. Moose might help, but having a generic serialization tool and a custom deserialization tool? That's no good (though I wouldn't mind custom tools to be built on a generic system).

If I want deserialization, I'll have to figure out how to add that to the Bermuda "islands".

Of course, this problem is wider than just Perl. Even being able to deterministically list all methods and know which ones are actually for "fields" (or slots, or properties, or whatever your term of choice is), doesn't tell me much about the intent of those methods. This, I think, is THE big hurdle that programming needs to solve. Behavior is in the code and intent is in our heads.


Intent

chromatic on 2008-02-04T20:03:45

This, I think, is THE big hurdle that programming needs to solve. Behavior is in the code and intent is in our heads.

This is also why syntax is such a small part of maintainability. It's the difference between phonics and vocabulary.

Perl 6 will (likely) not save you either

Stevan on 2008-02-04T23:34:01

So how do I figure out those methods short of running the code? I can't.

Without some kind of static analysis, you are just not going to be able to do that, but even that most likely won't get you what you want.

Think for a moment about introspecting classes without executing code. If you look at a language like Perl 6, where classes can be created, destroyed and modified at runtime. You need to have a highly dynamic meta-model and a meta-model is a living thing which means you have to execute some code.

Even with something as vanilla as the Java object model, you still have introspection and reflection capabilities which will defeat any type of static analysis. And IIRC the Java code must be compiled and loaded into the JVM before you can actually introspect it too, which is pretty much equal to "running the code" in JVM land.

Hell, even if I could, this could easily trip me up:
[/snip code example]
I could check to see if the method was defined in the class hierarchy:
[/snip code example again ...]

You do realize that Class::MOP will give you these things, in a much cleaner and simpler way?

package Foo;
use HTML::Entities 'encode_entities';
# ...
package main;
my @methods = Class::MOP::Class->initialize('Foo')->compute_all_applicable_methods()

This will return a list of all the methods names that Foo will respond to, taking into account inheritance and properly excluding imported methods. The list will be a list of HASH refs, each one containing the following; method name, the name of the class in which the method lives and a CODE reference for the actual method. And no, you don't need to use Moose to get this, nor do you need to alter your class at all, Class::MOP::Class will read all your package information and leave it just as it was found. No soapy residue :)

Perl is too random and has no real introspective capabilities. Perl 6 solves a lot of this, but I'm writing Perl 5.

I disagree with this completely. To start with Perl 5 has more introspection capabilities then any other language (short of LISP maybe) that I have encountered (and I am a language fetishist, so I have seen lots!). Not only can you walk the entire package namespace tree at runtime, examing and/or altering just about anything you could possibly want to, but you can actually do the same thing with the compiled opcode tree! Now, I will agree with you that the API for that kinda stuff is at best difficult and at worst absolute total crap, but it is there. While Perl 6 will surely provide a better API to this kind of stuff, I actually suspect that we will actually loose some of this introspection capabilities in the end (unless someone write B::PIR or something).

Moose might help, but having a generic serialization tool and a custom deserialization tool? That's no good (though I wouldn't mind custom tools to be built on a generic system).

I think you are missing the point of what Moose could offer you here. Moose brings consistency to Perl 5 OO, it cuts through the jungle of TIMTWODI-gone-bad while still allowing you a whole lot of freedom to wander off path. If you want to build a round-tripping serialization/deserialization tool, you MUST have some degree of consistency in the representation of your classes and objects. This is why MooseX::Storage 0.01 was able to be written over the couse of a 2 hour train ride from New Haven to NYC, it builds on the consistency that Moose provides and the introspection capable throug the MOP.

Of course, this problem is wider than just Perl. Even being able to deterministically list all methods and know which ones are actually for "fields" (or slots, or properties, or whatever your term of choice is), doesn't tell me much about the intent of those methods.

Okay, you will probably never find a tool that can divine programmer "intent", from just code. The programmer must supply meta-information to tell both the compiler and any possible humans reading the code, what it might be doing. So, to beat a dead Moose once more ....

Take the following class:

package Foo;
use Moose;
has 'bar' => (is => 'rw', isa => 'Str');

Here is one way to find out the "intent" of the 'bar' method in the Foo class above.

print Foo->meta->get_method('bar');

This prints "Moose::Meta::Method::Accessor=HASH(0x18f69b4)", which tells me that this is an accessor method generated by Moose. It also isa(Class::MOP::Method::Generated) as well, which tells me that Moose created it. But wait, this is not all, I can also tell what attribute this method is associated with, like so:

print Foo->meta->get_method('bar')->associated_attribute->name; # prints "bar"

I can even get a little silly and loop back around to find the same method ref.

refaddr Foo->meta->get_method('bar') == refaddr  Foo->meta->get_method('bar')->associated_attribute->associated_methods->[0];  # TRUE

And once you get the attribute meta-object (Foo->meta->get_attribute('bar')) then you can review all the metadata supplied by the programmer (such as the type "Str" and any of the defaults Moose supplies) and get a really good idea of what that method might do as well as how that attribute/slot/property is treated by the class. It's no crystal ball, but it is about as close as you will get in Perl 5 or 6, as I doubt that Perl 6 will end up implementing the entire Moose meta-model.

In conclusion, ... why the **** are you not using Moose!?!?! It seems like it has pretty much everything you want, and anything that is missing I suspect can be added by extending the MOP. I will even go so far as to offer to buy you dinner and drinks at your favorite pricey Portland restaurant and/or goth-bar during OSCON (assuming we both end up attending) if Moose doesn't already handle or can not be cleanly extended to handle all your needs.

What do you say? Ready to drink a little Moose-Aid?

- Stevan

Re:Perl 6 will (likely) not save you either

chromatic on 2008-02-08T19:34:27

While Perl 6 will surely provide a better API to this kind of stuff, I actually suspect that we will actually lose some of this introspection capabilities in the end (unless someone write B::PIR or something).

I can think of someone who has experience with Parrot, PIR, the Perl 6 object system, and the B:: modules.... I'm waiting for a couple of other pieces of Parrot to be ready before that will work.

Connection Problems?

stu42j on 2008-02-05T15:42:03

Yes: http://use.perl.org/user/stu42j/journal/35523

Re:Connection Problems?

Ovid on 2008-02-12T09:02:55

Hey, just noticed this link. I'm glad to know I'm not the only one.