After 3 months of delay, it was quite amusing that the initial MO bridge took only 3 hours to land:
pugs> vv('hello, world').HOW
^Str
pugs> vv('hello, world').HOW.HOW
^Class
(The vv()
above is a makeshift call that casts 6.2.0-land values into 6.28.0-land objects; it will go away once all existing built-in types are wrapped into MO.)
To understand what the ^Class
notation means, let's take a brief look at Perl 6's object model, as implemented by Pugs 6.28.0's MO (Meta Object) subsystem.
In a traditional class-based object system such as Ruby's, the String
class would be an instance of the Class
class:
irb(main)> String.instance_of?(Class)
=> true
irb(main)> String.instance_of?(String)
=> false
Because of this, you can't call .new
on a String instance, and you can't call .length
on the String class:
irb(main)> String.new.new
NoMethodError: undefined method `new' for "":String
irb(main)> String.new.length
=> 0
irb(main)> String.length
NoMethodError: undefined method `length' for String:Class
Perl 5, on the other hand, takes an unique approach: When we say IO::String->new
, the IO::String
is not a Class object -- rather, it is a prototypical IO::String
object that has no attributes!
IO::String->isa('Class');
#=> false (perl5 doesn't have a built-in Class)
IO::String->isa('IO::String');
#=> true
IO::String->new->new;
#=> IO::String=GLOB(0x18ab160)
IO::String->new->getpos;
#=> 0
IO::String->getpos;
Error: Can't use string ("IO::String") as a symbol ref...
The advantage of this arrangement is that several object systems -- prototype-based, closure-based, et cetera -- can exist simultaneously in the same program, without having to inherit from an universal Class
class.
However, a bare literal "IO::String"
is a terrible way to represent a prototypical object: it makes reflection needlessly difficult, and the error message for accessing the non-existing attribute slot ("Can't use string as a symbol ref...") seems to obey the principle of most surprise.
Perl 6's solution to this is simple: The prototypical string object, spelled ::Str
or simply Str
(if it's in scope), is a genuine Str instance. However, any attempt to access its attributes raises a sensible exception. Just as in Perl 5, so-called class methods such as .new
are simply those methods that does not access instance attributes, and you can call them on both ::Str
and regular Str instances.
Calling an object's WHAT
method returns the prototypical object. This replaces Perl 5's ugly ref($x) || $x
idiom:
pugs> 'hello'.WHAT
::Str
pugs> Str.WHAT
::Str
On the other hand, because Perl 6's builtin objects are backed by a normal class-based dispatch system, you can reliably obtain a list of all Str's supported methods, by querying the Class
instance that implements ::Str
:
pugs> vv('hello').HOW
^Str
pugs> vv('hello').HOW.methods
["HOW","WHICH","bless","reverse"]
So there we have it: the Perl5ish prototypical object Str.WHAT
is also spelled ::Str
, and the Rubyish class object Str.HOW
is also spelled ^Str
.
The next step is to expose all MO's meta-objects (Role/Method/Class/Object) into Perl 6 land, and adapt our Perl 5 bridge to use Moose.pm, such that a class Foo {...}
declaration in Pugs can generate both Haskell-side and Perl5-side representations, and work seamlessly with libraries on either side. Stay tuned!
Recent Comments