Stevan Little wrote:
> Jonathan Lang wrote:
> > 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.

I think that we're talking past each other: you're trying to educate
me on how a programmer should think about objects and classes; I'm
trying to figure out how a non-programmer thinks of them.

To non-programmers (and amateur programmers), objects aren't instances
of classes; classes are categories of related objects.  Objects have
behaviors and characteristics; classes are a convenient shorthand for
describing behaviors and characteristics common to a set.  Objects
come first, while classes are thought of in the context of objects. 
The same implementation can be used for both perspectives: "dogs can
bark; Spot is a dog; therefore, Spot can bark" is a form of reasoning
that underlies using the class-and-instance model for the "back-end"
implementation of the object-and-category paradigm.

"classes have methods; objects respond to them" is part of the
classes-and-instances paradigm; but that isn't really how people
think.

In terms of practical differences: under the classes-and-instances
paradigm, if you want to create an object whose behavior differs
slightly from a given class, you need to create a subclass that has
the desired behavior and to create the object as an instance of that
subclass.  With the object-and-category paradigm, there's nothing that
insists that every object's behavior must conform precisely to the
behaviors described by its classes; the latter are "merely" rules of
thumb to apply to the former until you learn differently, and
behaviors can be added, removed, or tweaked on an individual basis. 
This is why (last I checked) "but" creates a behind-the-scenes
'singleton' subclass for the new object instead of demanding that a
new subclass be explicitly created, and why I suggested the
possibility of adding, replacing, or removing methods for individual
objects as well as for classes (which, presumably, would also be
implemented under the hood by replacing the object's class by a
'singleton' subclass).

> > >   ^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.

OK.  The only reason I was thinking in those terms was because of the
possibility that $spot might be based on one of those
behind-the-scenes customized subclasses that I mentioned earlier: if

  my Dog $brutus but cannot("bark");

How do you access the subclass of Dog that $brutus is the instance of?

> > 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.

I was under the impression that the distinction between a method and a
multi-method was how many of the parameters get used to dispatch:
methods aren't really "owned" by classes, any more than class
definition is a declarative process; it just looks that way on the
surface.  Am I wrong about this?

> > or maybe
> >
> >   method Dog.bark () { ... }
>
> Yes that works too. But TIMTOWTDI, and each has it's own benefits.

I'm aware of that, and was proposing this as Another Way.

> 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).

I was under the impression that class definition was fundamentally a
functional process dressed up as a declarative process.  "method
Dog.bark () { ... }" would seem to me to be a means of continuing that
process "after the fact" - that is, adding a method to a class after
you've left the class definition block.  It seems to serve exactly the
same purpose as using the metaclass API.  That is, I see it as being
alternate syntax for "^Dog.add_method(bark => method () { ... })".

> > 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.

_This_, OTOH, was originally intended as a replacement for
^Dog.add_method(), not merely as an alternative to it.  That said, I'm
not averse to it being an alternative; there's something nice about
being able to use a single call with a hash of coderefs to add a batch
of methods all at once.

> 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.

It's action-at-a-distance that only occurs when you're using can() in
the context of an rvalue - that is, when you're querying about the
status of existing methods.  When used as an lvalue - that is, when
assigning something to it - no searching would take place; the
assignment would be made to whatever called it.  Assign to a class,
and you've modified that class (not any of its superclasses); assign
to an object, and you've added, replaced, or removed a singleton
method.  That is,

  Dog.can("bark") = method () { ... }

would be exactly equivalent to

  ^Dog.add_method(bark => method () { ... });

Still, perhaps I was stretching the metaphor somewhat.  "How does a
dog bark?" is a different question from "can a dog bark?"  &can
addresses the latter question; &fetch_method would address the former.
 So instead of trying to force it all into &can, where it probably
doesn't belong, you might be better off doing something like:

  &coderef = ^Dog.method{'bark'};
  ^Dog.method{'bark'} = method () { ... };
  delete ^Dog.method{'bark'}
  ^Dog.method = (sit => method () { ... }, roll_over => method () { ... });

And so on, using hash operations to fetch, add, replace, or remove
individual methods.  Likewise with attributes, classes, and roles.

In the objects-and-categories paradigm, "^" would signify that I'm
wanting to deal with the nature of whatever the thing is, rather than
dealing with the thing itself.  So "Dog" means "a dog"; "^Dog" means
"a dog's nature".  "can $spot 'bark'" would be similar to "exists
^$spot.method<bark>", or, in English, "does the ability to bark exist
in Spot's nature?"  The only possible difference might be that the
latter only consults Spot's nature itself, whereas the former consults
Spot's nature as well as the nature of any classes that Spot is in and
the nature of any roles that Spot can do.

--
Jonathan "Dataweaver" Lang

Reply via email to