On 2/9/06, Jonathan Lang <[EMAIL PROTECTED]> wrote:
> Stevan Little wrote:
> > Jonathan Lang wrote:
> > > OK.  To help me get a better idea about what's going on here, what
> > > sorts of attributes and methods would ^Dog have?
> >
> > Well, a metaclass describes the behaviors and attributes of a class,
> > and ^Dog is an *instance* of the metaclass. So actually ^Dog would not
> > actually have attributes and methods since it is an instance.
>
> Huh?  A dog can bark; so the Dog class should have a method called
> 'bark'.  Or does 'can' not mean what it seems to mean?

^Dog is an instance of the MetaClass, while Dog (no ^ sigil) is the
"class" (actually it's a prototypical instance of the class which the
metaclass ^Dog describes, but you dont really need to know that to use
it).

  ^Dog.can(bark) # false
  Dog.can(bark) # true

This is a very important distinction. Saying "Dog class has a method
called 'bark'", implies the following statements are true

- Dog will respond to the method called "bark".

- Given my Dog $spot, $spot will respond to the method called "bark".

- ^Dog (the metaclass instance which describes a class called "Dog")
does *not* respond to the method called "bark".

- ^Dog (since it describes the class called "Dog") manages all of the
methods which Dog and $spot will respond too, one of which is called
"bark".

There is a clear line between the meta-level (where ^Dog lives) and
the user-level (where Dog and $spot) live. This is a line which is
heavily blurred in Perl 5, and in many OO languages (aside from
Smalltalk, CLOS and a few others) the meta-level is just not
accessible at all from user-land.

> > That said, I think ^Dog would probably respond to methods like
> > these (some of which are described in S12):
>
> OK; apparently, what I meant when I asked "what methods and attributes
> does ^Dog have?" is what you're talking about when you speak of which
> methods ^Dog will respond to.  To me, an object has whatever methods
> that it responds to.

I disagree, an object is an instance of a class. A class "has" the
methods that the object will respond too. You would not want to store
all the methods in each instance, it would not make sense. Each
instance needs to share a set of methods, and those methods are stored
in the class.

Well what is a class?

In Perl 5 a class is simply a package, and the subs in that package
are methods. In Perl 6 however, a class will be an instance of another
class, the MetaClass. These two things are really not that different
when you think about it.

- A Perl 5 package holds methods for you by storing them in the symbol
table. You can add, get, remove these methods from the symbol table
using the symbol table API.

- A Perl 6 class holds methods for you by storing them inside an
instance variable in an instance of the MetaClass, and you can add,
get, remove these methods by using the methods of MetaClass.

Of course you have a conceptual circulatiry issue now because well,..
what is a MetaClass? Well it could be an instance of a MetaMetaClass,
but what is that an instance of? This could go on forever (turtles all
the way down as it is sometimes called).

But in practice you either just stop and say "this is as far as it
goes",  or you bootstrap your class model somehow and "tie the knot".
I prefer the boostrapping as it is much more elegant and tends to
allow for much more flexibility.

> >   ^Dog.name # Dog
> >   ^Dog.version # 0.0.1 (or something similiar of course)
> >   ^Dog.authority  # cpan:LWALL or email:[EMAIL PROTECTED]
> >
> >   ^Dog.identifier # returns the string Dog-0.0.1-cpan:LWALL
>
> Would it be valid to speak of ^$spot?  If so, what would ^$spot.name be?

There is no such thing as a ^$spot. The ^ is the "class" sigil, and
will hold metaclass instances only, just as variables with  % will
only holds hashes, and variables with @ will only holds arrays, etc.

> > I would like to see some methods like this:
> >
> >   # dynamically add a method that
> >   # Dog and $spot would respond to
> >   ^Dog.add_method(bark => method () { ... });
> >
> > Which would be like doing this in Perl 5:
> >
> >   no strict 'refs';
> >   *{'Dog::bark'} = sub { ... };
>
> IIRC, you can always create a new method for a class, even outside of
> its definition, simply by ensuring that the first parameter to be
> passed in will be an object of that type:
>
>   method bark (Dog $_) { ... }

I don't think this is true unless it is a multi method, in which case
it is not actually a method of the of the class, but instead just
DWIMs because of MMD and the fact we allow an invocant calling style
freely.

> or maybe
>
>   method Dog.bark () { ... }

Yes that works too. But TIMTOWTDI, and each has it's own benefits.
Your above approach works fine while you are writing the code, but is
not as useful for dynamically adding a method at runtime (unless you
use eval(), but that gets ugly). Using the metaclass API dynamically
adding a method to a class at runtime is trivial, again, think of it
as being no different that doing this in Perl 5:

# deep within a function somewhere ...
if ($some_condition_is_true) {
    no strict 'refs';
    *{$class . '::' . $method_name} = $method;
}

It would just look a little different.

# deep within a function somewhere ...
if ($some_condition_is_true) {
    $class.add_method($method_name => $method);
}

In fact, if you want to experiment with this type of metaclass API, I
have a module for doing this in Perl 5 here
(http://search.cpan.org/~stevan/Class-MOP), it is fairly well
documented and the code is heavily commented.

> > And of course if you can add a method, you will need to be able to
> > fetch and delete them as well, so a &get_method and &remove_method
> > would be in order as well.
>
> To fetch a method, why not have .can() return a reference to the
> method upon success? I might even go so far as to treat can() as an
> lvalue, using the assignment of a coderef as an alternate way of
> adding or changing the object's behavior on the fly:
>
>   method bark (Dog $_:) { ... };
>   Dog.can("bark") = method () { ... }; # Teach the dog a new trick
>   Dog.can("bark") = true; # inform the dog that it ought to know how
> to bark, without telling it how, yet; equivalent to a literal "=
> method { ... }".
>   Dog.can("bark") = false; # tell the dog to forget how to bark.
>   Dog.can("bark") = undef; # Ditto.

I see one major problem with this, and that is that .can() will search
up the class heirarchy of which Dog is a part of.  So even though
.can() returns true, that does not mean the method is defined in Dog,
it could be defined in Mammal or LifeForm or even the base Object. If
.can() was an lvalue then what method am I modifying? This is big-time
"action at a distance" and I don't like it at all.

> (Doing this to Dog DWIMs to modifying the behavior of all dogs at once
> - you're declaring that "dogs can bark" or "this is how dogs bark",
> whereas doing it to $spot DWIMs to modifying the behavior of $spot
> only: "$brutus.can('bark') = false": my best friend's pet dog seems to
> have lost the capacity to bark in its old age; that doesn't mean that
> dogs in general can't bark.)

Adding methods to instances is a feature which Ruby has (they call
them singleton methods).  And which I think Perl 6 should have too,
but I think the same problems with your API proposal exist with $spot
as they do with Dog.

> Similar things might be done with .has (for attributes), .isa (for
> superclasses), and .does (for roles).

Again, .isa() is not local, so just because Dog.isa(Mammal) returns
true does not mean that Mammal is a direct superclass of Dog. The same
applies to Roles and attributes as well.

It is important to remember that the metaclass instance ^Dog only
describes Dog, and not any of it's superclasses or roles. It is only
the local class, and nothing more.

Stevan

Reply via email to