Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Sorry if this is not the correct place to ask.

I use Ruby, and am a complete Perl newbie. One of the coolest thing in Ruby is that you can "reopen" any class, including builtin ones and add methods to it. This is very powerful. For example, by just require-ing the 'yaml' library, suddenly all objects, be it a string, hash, symbol, has a to_yaml() method to represent itself as YAML. Or, you can redefine methods and make builtin classes behave differently (or crazily).

Is Perl5 able to do the equivalent of this, reopening classes and redefining core functions like open(), die()? What is the syntax for that? Also, how about Perl6?

  • Comment on Reopening builtin classes, redefining builtin functions?

Replies are listed 'Best First'.
Re: Reopening builtin classes, redefining builtin functions?
by JStrom (Pilgrim) on Dec 08, 2007 at 03:02 UTC
    A class in Perl is nothing more than a namespace. All you need to do is add to that namespace:
    use Data::Dumper; # $d is an instance of Data::Dumper $d = new Data::Dumper([]); # Use a typeglob to create &Data::Dumper::foo *{Data::Dumper::foo} = sub { print "foo\n" }; # And now $d has the method $d->foo(); # Switch into the Data::Dumper package (namespace) package Data::Dumper; # Define a function here sub bar { print "bar\n" } # Back in main, $d has the new method package main; $d->bar(); # No different if the method exists *{Data::Dumper::Dump} = sub { print "Not Dump\n" }; $d->Dump();
    Perlsub has an example of replacing the built-ins.
Re: Reopening builtin classes, redefining builtin functions?
by stvn (Monsignor) on Dec 08, 2007 at 04:27 UTC

    This kind of functionality (in the object oriented way that Ruby does it) is no available in the core, but though CPAN modules. For instance autobox provides the ability to treat any Perl reference as an object. The autobox module itself does not define anything, but you can either build your own system or use autoload::Core or Moose::Autobox.

    In addition to this, you might want to look at Moose itself, it provides a object meta-model similar to what will be in Perl 6. With it, you can do many of the Ruby meta-programming tricks and some that are not even possible in Ruby.

    -stvn
      What can Moose do that Ruby can't?

        Moose is based on Class::MOP which is a Meta Object Protocol written in Pure Perl. It is also a meta-circular MOP, which means that at some point it bootstraps and redefines part of itself using itself. The end result is that Class::MOP::Class is itself an instance of Class::MOP::Class (to be more technically correct, Class::MOP::Class->meta is an instance of Class::MOP::Class because a class name in Perl is not an object, but just a string and so we need to store the metaclass instance somewhere). Again, this is all defined in Pure Perl, so it is 100% accessible to your regular Perl code. Ruby defines Class in object.c (as well as Module, Object and a few other core bits), and the guts of these things are only as accessible as the language wants them to be (which is quite a lot, but not nearly as much as Class::MOP though). This difference is critical to the power of the MOP.

        In Ruby, in order to get the metaclass, you have to do the idiomatic class <<self; self; end, and then you can hack on the metaclass. This is tricky and can get really ugly really quickly (DISCLAIMER: I am not an expert Ruby hacker, so there may be some secrets that I am missing out on, but I found little online or in books to contradict this statement). Most of the stuff I have seen which does anything interesting with this, ends up using the *_eval methods found in Module. Now, I have no problem with string-eval-ing code when you are deep in the meta-world, I do it a lot in Moose to optimize accessors and such. However in Moose it is a choice, in Ruby it is not. This means that there is a fundamental limit to how reusable your metaclass trickery can be and more importantly how maintainable it is (string evaled code is really annoying to maintain, I know).

        One of the key things you can do in Moose that (AFAIK) you cannot do in Ruby is to extend the meta layer. Ruby gives you access to the metaclass and through modules and mixins you can somewhat alter a classes behavior in a reusable fashion. But thats where it ends, with Moose you can literally extend Moose with Moose. Here is a classic metaclass example of a class which counts it's instances.

        package CountingMetaClass; use Moose; extends 'Moose::Meta::Class'; has 'count' => ( is => 'rw', isa => 'Int', default => sub { 0 } ); after 'construct_instance' => sub { my $class = shift; $class->count($class->count + 1); };
        Then in your code you can do:
        package Foo; use metaclass ('CountingMetaClass'); # tell it what meta you want used + first ... use Moose;
        An equivalent Ruby version would require some serious meta-programming to both wrap the new method, as well as create and install a custom metaclass (honestly I have no clue as to how it would be done, and even if it could be done).

        (Yes, I know thats a contrived example, but this is a simple example to illustrate my point, there is lots of useful and amazing metaclass hackery going on in the #moose channel on irc.perl.org, stop by if you want to see some).

        Now, aside from metaclasses, Moose (though Class::MOP) also provides some sub-protocols. The most powerful one (IMO anyway) is the Attribute protocol, you can find a nice cookbook entry for it here which goes into a lot of detail about how you can easily extend this protocol to get some nice features. You can also use the Instance protocol to customize the instance type which Moose uses, which is by default a HASH ref. The module MooseX::GlobRef::Object provides an excellent example of how to extend this protocol to make your classes use a GLOB ref. Of the cool parts about this is that it works on a per-class basis, and is not a system wide change, so it won't break your other stuff.

        Now throw into this Roles, which are a more sane version of mixins, the fact that you can have true Multiple Inheritance if you want it, and most importantly (at least for me) access to the CPAN (yes, I know, rubygems, etc,.. but it is no CPAN).

        -stvn
Re: Reopening builtin classes, redefining builtin functions?
by dragonchild (Archbishop) on Dec 09, 2007 at 03:12 UTC
    The answer to your questions are:
    • You don't close classes in Perl, so there's no reopening of them.
    • There are no builtin classes (well, mostly - q.v. the next answer)
    • All classes in Perl inherit from UNIVERSAL. So, if you want everyone to have a to_yaml method, do this:
      { no strict 'refs'; *{ "UNIVERSAL::to_yaml" } = sub { # Put some code here. }; }
    • To redefine a core function, do this:
      { no strict 'refs'; *{ "CORE::<funcname>" } = sub { ... }; }
      Some functions require you to redefine in CORE::GLOBAL:: instead. There's no real rhyme or reason (from the outside looking in) - you can find more by looking at the appropriate documentation.
    • Perl6 will allow for redefinition of EVERYTHING under the sun, including syntax, yet have those redefinitions be scoped however you want. In other words, the default Perl6 language is just that - the default. TimToady and the rest of the design team has the idea that Perl6 will actually be a group of interrelated languages that can all talk to one another vs. just a single language. And, people will provide modules to extend the language vs. having to edit the source.

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?