I would like to propose that class methods do not get inherited along
normal class lines.
I think that inheriting class methods will, in many cases, not DWIM.
This is largely because your are inheriting behavior, and not state
(since class attributes are not inheritable). Let me explain in more
detail.
Let's start by making a very basic definition of an *object*,
ignoring any implementation details or specifics.
object == state + behavior
This statement assumes that *objects* at their core are a unique
state coupled with a collection of behaviors to act upon that
particular state. Of course we are ignoring all the other class/meta/
inheritence junk for now.
To take away the behavior, and only be left with state would degrade
our object to the level of C struct or Pascal-style record-type. To
take away the state, and only be left with behavior, would basically
leave a module/package or some pseudo-random collection of functions.
So at this point, I think it is safe to say that an *object* should
have both state and behavior.
Now, back down from the theoretical cloud to reality. I would like to
show some canonical class-method examples (and in some cases, show
how they are broken), then show how they might be better accomplished
in Perl 6 without the need for class methods to be inherited.
== Instance Counting Class
The most common example given for class methods is an "instance
counter". Here is how one might (naively) look in Perl 6:
class A {
our $.count;
method count (Class $c:) { $.count; }
submethod BUILD {
$.count++;
}
}
Each time an instance of A is created the counter is incremented. So
that ...
A.count; # 0
A.new;
A.count; # 1
Now this makes sense, until we subclass A.
class B is A {}
A.count; # still 1
B.new; # calls A::BUILD
A.count; # 2
B.count; # 2
Clearly, we only have one instance of A, and one instance of B, so
those numbers are wrong. It could be argued that since B is a subtype
of A, we do have two A's, but the argument does not work in reverse.
But either way, I would argue that the results shown above are
misleading, and probably not what the programmer intended.
What is happening here is that we are inheriting behavior, but not
inheriting state. Which goes against the core definition of *objects*.
"I can solve this, just make class attributes inheritable?", you say.
Sure, you could do that, however, it complicates the meta-model
unnecessarily. It is much easier to accomplish this using a subclass
of Class.
class CountingClass is Class {
has $.counter;
method count (CountingClass $c:) { $.counter; }
method new (CountingClass $c: %params) {
$.counter++;
next;
}
}
class A meta CountingClass {}
class B meta CountingClass {}
Now A and B both have their own counters neither of which interfere
with one another. Of course the "meta" syntax there is speculative,
but surely you can accomplish that behavior somehow. This approach
actually uses no class methods, only instance methods.
However, as always, there is more than one way to do it, you can
accomplish the same thing using Roles. Here is how that might look:
role Countable {
our $.count;
method count (Class $c:) { $.count; }
submethod BUILD {
$.count++;
}
}
CountingA = A but Countable;
CountingB = B but Countable;
CountingA.count; # 0
CountingA.new;
CountingA.count; # 1
CountingB.count; # 0
CountingB.new;
CountingB.count; # 1
NOTE: I am assuming that the Countable role will mix-in the class
method &count as well as the class attribute $.count. And that "A but
Countable" is really sugar for something like "class { does
Countable; is A }".
== Custom Constructors
Another common example of class method usage is custom constructors.
This example is moot given the BUILDALL/BUILD system. All class
specific initialization can easily be done using custom BUILD
submethods.
This example too is skewed towards a language's particular object
model as well. In Java/C# the constructor is a special/magical
"thing" which is called by the "new" keyword. In Smalltalk, "new" is
actually an instance method of the class Class, and it calls the
specific object's "new" instance method to initialize (somewhat like
the CREATE->BUILDALL/BUILD in Perl 6). In the Perl6-MetaModel
prototype, &new is implemented as an instance method of Class, and
not a class method of Object (as is sometimes assumed).
== Java-style static methods
Java's static methods are only thought of as being like class methods
because they have access to other static class members, and they are
only callable "though" the class. The Perl 6 equivalent of this
concept is nothing more than a package sub, and a package variable.
Since Class isa Package, this type of behavior is easily accomplished.
== Conclusion
Now, I am not proposing we abolish class methods entirely, only that
we simplify them. If we do not require that class methods be
inherited, then they can be implemented as ruby-style "singleton-
methods" on the Class instance. This keeps the internals of the meta-
model clean and orderly (always a good thing IMHO :).
Anyway, I have said my peace, what do you all think?
Thanks,
Stevan
I think there are serious problems with this proposal. For a start, it would
be very difficult to create *any* objects at all if the C<new()> class method
wasn't inheritable.
Damian
One of the things that has annoyed me with Java is that it's class
methods don't inherit (dispatch polymorphically). This means that you
can't apply the "template method" pattern to static (class) methods. I
hope Perl 6 doesn't copy this "feature".
If you would please give a real-world-useful example of this usage of
class-methods, I am sure I could show you, what I believe, is a
better approach that does not use class methods.
As for Java's static methods, they are very different from class
methods in Perl 6. Java's static methods are little more than
functions which have access to other static members. This "feature"
is available in Perl 5 now in the form of subs in a package namespace
and package scoped variables. The fact that Java uses the method call
syntax to access static methods is (IMHO) just an attempt to have
consistency in calling conventions. I say this because (as you
pointed out) they share very little with other methods.
Stevan
The example I've wanted to code in Java is along the lines of:
public class Base {
public static main(String[] args) {
init();
do_it(args);
cleanup()
}
}
and then define a bunch of derived classes as my main class.
public class Derived extends Base {
static init() { print("doing init"); }
static do_it(String[] args) { print("doing body"); }
static cleanup() { print("doing cleanup"); }
}
% javac Derived
% java Derived
In other words, I wanted to not have a main function on the class that I
run as the application.
This example, of course, doesn't apply to Perl -- but I think that the
basic pattern is still useful
Actually &new should be an instance method of Class, not a class
method of Object. Since all classes are instances of Class, then
calling Foo.new is just calling an instance method on the instance of
Class whose name is Foo.
As for overriding a class specific &new, this is accomplished using
singleton-methods on the instance of Class. That &new is then not
inherited. I think that this is probably sane, and the BUILD
submethod is a far better place to put class specific initialization
since your are guaranteed to have all BUILD methods run correct and
in-order.
This follows how most other reflective object models work.
In Smalltalk-80 every class has a metaclass, the class itself stores
all instance methods for the instance, while the metaclass stores all
"class methods" (which are basically instance methods on the
metaclass instance). This allows the smalltalk method dispatcher to
work exactly the same for both class methods and instance methods.
Smalltalk does allow you to define the "new" message for a particular
class, but it becomes an instance method of the metaclass.
In CLOS, the generic method 'make-instance' is specialized to
'standard-class', it runs the initializer of each superclass in the
class precedence list, but those are instance specific methods.
Ruby uses singleton-methods on the Class instance to create "class
methods", which is where the whole eigenclass thing comes from. The
result is that class methods are themselves instance methods on the
eigenclass (which is very similar to how Smalltalk's metaclass system
works).
Also in most of the other literature I have read, the responsibility
of creating an instance is relegated to Class, and not to Object (or
some other element of the system).
Stevan
I think most class methods should be written as submethods instead.
: I think that inheriting class methods will, in many cases, not DWIM.
: This is largely because your are inheriting behavior, and not state
: (since class attributes are not inheritable). Let me explain in more
: detail.
:
: Let's start by making a very basic definition of an *object*,
: ignoring any implementation details or specifics.
:
: object == state + behavior
:
: This statement assumes that *objects* at their core are a unique
: state coupled with a collection of behaviors to act upon that
: particular state. Of course we are ignoring all the other class/meta/
: inheritence junk for now.
:
: To take away the behavior, and only be left with state would degrade
: our object to the level of C struct or Pascal-style record-type. To
: take away the state, and only be left with behavior, would basically
: leave a module/package or some pseudo-random collection of functions.
:
: So at this point, I think it is safe to say that an *object* should
: have both state and behavior.
You seem to be arguing that a class has no state, but my view is that,
in the abstract, a class encompasses the state of *all* its objects.
It just hasn't picked one particular object to be at the moment.
: Now, back down from the theoretical cloud to reality. I would like to
: show some canonical class-method examples (and in some cases, show
: how they are broken), then show how they might be better accomplished
: in Perl 6 without the need for class methods to be inherited.
:
: == Instance Counting Class
:
: The most common example given for class methods is an "instance
: counter". Here is how one might (naively) look in Perl 6:
:
: class A {
: our $.count;
: method count (Class $c:) { $.count; }
: submethod BUILD {
: $.count++;
: }
: }
That's obviously broken--the count accessor should be a submethod to be
consistent, unless the explicit intent is that any subclass of A
return the count of A's. Which, not surprisingly, is exactly what
you get below. It should probably have been declared:
our $.A_count;
in that case. And in which case you don't need the explicit accessor,
since one would have been provided because of the dot. If you don't
want the autoaccessor, don't use the dot.
I suppose an argument could be made that autoaccessors for class vars
should be submethods by default. Or maybe "my $.count" makes a submethod,
while "our $.A_count" makes a method.
: Each time an instance of A is created the counter is incremented. So
: that ...
:
: A.count; # 0
: A.new;
: A.count; # 1
:
: Now this makes sense, until we subclass A.
:
: class B is A {}
:
: A.count; # still 1
: B.new; # calls A::BUILD
:
: A.count; # 2
: B.count; # 2
:
: Clearly, we only have one instance of A, and one instance of B, so
: those numbers are wrong. It could be argued that since B is a subtype
: of A, we do have two A's, but the argument does not work in reverse.
Sure it does. It doesn't matter whether B is a subtype of A or not,
you've given it an interface to code that counts A's.
: But either way, I would argue that the results shown above are
: misleading, and probably not what the programmer intended.
That's certainly possible, but it wouldn't be the first time people
have been surprised that the computer did exactly what they asked it
to... :-)
: What is happening here is that we are inheriting behavior, but not
: inheriting state. Which goes against the core definition of *objects*.
I'd say that state is precisely what we're inheriting here. It's just
the state of something that is not the object. But that was already
true of class A as well.
: "I can solve this, just make class attributes inheritable?", you say.
Don't see how that would help, unless you also force every class to
redeclare the attributes. (By the way, attributes of the form $.count
are already inheritable, being shorthand for $?SELF.count.)
: Sure, you could do that, however, it complicates the meta-model
: unnecessarily. It is much easier to accomplish this using a subclass
: of Class.
:
: class CountingClass is Class {
: has $.counter;
: method count (CountingClass $c:) { $.counter; }
: method new (CountingClass $c: %params) {
: $.counter++;
: next;
: }
: }
:
: class A meta CountingClass {}
: class B meta CountingClass {}
:
: Now A and B both have their own counters neither of which interfere
: with one another.
Only by forcing people to repeat themselves as a matter of policy.
And policy could just as easily have added "our $.count" to B. The
real trick would be to set up A such that you don't have to do anything
special in B. I suppose we could say that, by default, if A isa B,
then A gets also gets whatever metaclass B has, not the standard
metaclass supplied by "class".
But these are metaclasses, not classes. You keep writing the type of
the invocant of class methods as Class, but I don't believe that anymore.
The type of the invocant of a class method in A is A, not Class. It
just happens to be a generic A, not a specific A, and tests as undefined.
: Of course the "meta" syntax there is speculative,
: but surely you can accomplish that behavior somehow. This approach
: actually uses no class methods, only instance methods.
You can already do this sort of thing using the trait syntax.
That's why I go on about "traitorous traits" in A12: "is foo" can
do *anything* to the container on its left, including installing
new parent classes and metaclasses. (But if I did want to define an
important new default-overriding metaclass, I'd probably replace the
"class" keyword with a word like "role", or "union", or "theory",
or whatever I thought was appropriate. Not sure counters rise to
that level though.)
: However, as always, there is more than one way to do it, you can
: accomplish the same thing using Roles. Here is how that might look:
:
: role Countable {
: our $.count;
: method count (Class $c:) { $.count; }
: submethod BUILD {
: $.count++;
: }
: }
:
: CountingA = A but Countable;
: CountingB = B but Countable;
Again, you're forcing the redundancy on the user.
: CountingA.count; # 0
: CountingA.new;
: CountingA.count; # 1
:
: CountingB.count; # 0
: CountingB.new;
: CountingB.count; # 1
:
: NOTE: I am assuming that the Countable role will mix-in the class
: method &count as well as the class attribute $.count. And that "A but
: Countable" is really sugar for something like "class { does
: Countable; is A }".
Well, hey, we're out ahead of the state of the art here, so we can
do whatever we like with roles.
: == Custom Constructors
:
: Another common example of class method usage is custom constructors.
: This example is moot given the BUILDALL/BUILD system. All class
: specific initialization can easily be done using custom BUILD
: submethods.
The purpose of custom constructors in Perl 6 is to allow the use of
positional args. I can't think of any other use for them, offhand...
: This example too is skewed towards a language's particular object
: model as well. In Java/C# the constructor is a special/magical
: "thing" which is called by the "new" keyword.
Closer to Perl's .bless builtin, really.
: In Smalltalk, "new" is
: actually an instance method of the class Class, and it calls the
: specific object's "new" instance method to initialize (somewhat like
: the CREATE->BUILDALL/BUILD in Perl 6). In the Perl6-MetaModel
: prototype, &new is implemented as an instance method of Class, and
: not a class method of Object (as is sometimes assumed).
I think that, at least as a name, the default new() *should* be in
Object, though of course most of its work might done by Object.meta
instead. I don't believe in Class as a class anymore, unless that's
what .meta reaches these days. If you want to change the behavior
of a class, you're really talking about changing the behavior of
the metaclass instance (which typically stores various bits of its
data in a package). The A class is just a proxy for all that, so
that you can reason about objects of type A without actually having
one.
I'm beginning to suspect Class is just a mixin to that breaks the
instance association, such that if you did
$fido = new Dog;
$fido does Class;
you'd end up with $fido undefined (but still of type Dog).
So basically, you declare your constructor
method new (Class $c: ...)
if you want to restrict $c to something that does the Class *role*, which
guarantees there's no associated instance. But if you're more into
prototyping, you'd say
method modified (Dog $d: ...)
if you want to restrict $d to something that does Dog, and you don't
care whether it happens to have an instance associated because the
class already defines decent defaults for any attributes you don't
override.
I want Perl 6 to encompass both class-based and prototype-based
approaches, and this seems like the right way to do it.
: == Java-style static methods
:
: Java's static methods are only thought of as being like class methods
: because they have access to other static class members, and they are
: only callable "though" the class. The Perl 6 equivalent of this
: concept is nothing more than a package sub, and a package variable.
: Since Class isa Package, this type of behavior is easily accomplished.
I think private methods can fill this role in Perl 6, and already have
the privacy/trust thing built-in.
: == Conclusion
:
: Now, I am not proposing we abolish class methods entirely, only that
: we simplify them. If we do not require that class methods be
: inherited, then they can be implemented as ruby-style "singleton-
: methods" on the Class instance. This keeps the internals of the meta-
: model clean and orderly (always a good thing IMHO :).
:
: Anyway, I have said my peace, what do you all think?
I think there's no such thing as a Class instance. That simplifies
things even further.
Larry
I think this example is constrained by the way Java handles the main
static method. This same pattern could be done using instance
methods, and a little bit of reflection in Java.
public interface Base {
public void init;
public void do_it(String[] args);
public void cleanup;
}
public class BaseRunner {
public static main (String[] args) {
ClassLoader cl = new java.lang.ClassLoader();
Class c = cl.findClass(args[0]);
Base b = c.newInstance();
b.init();
b.do_it(args);
b.cleanup();
}
}
public class Derived implements Base {
public void init() { print("doing init"); }
public void do_it(String[] args) { print("doing body"); }
public void cleanup() { print("doing cleanup"); }
}
NOTE: this code is untested :)
This version actually allows you to vary the subclasses though the
command line arguments, which provides even greater flexibility and
does not require you to recompile BaseRunner or Base.
Doing something similar in Perl 6 is even easier than the Java version.
Stevan
> I would like to propose that class methods do not get inherited along
> normal class lines.
You mean, make them *not methods?* Because it's not a method unless it
has an invocant, as far as I'm concerned. (Method implies polymorphism.)
: Let's start by making a very basic definition of an *object*,
: ignoring any implementation details or specifics.
:
: object == state + behavior
I don't see how this is a bad thing. Classes don't have state. That's
their lot in life. Que sera! Inheritance of behavior alone is useful.
My primary want for class methods, as a whole, is to provide this sort
of interface in Objective-C:
@interface Host : NSObject {
- (Class)plugInClass;
- (void)setPlugInClass:(Class) plugInClass;
}
@interface PlugIn : NSObject {
- (BOOL)initWithHost:(Host *)host;
+ (BOOL)supportsFeatureA;
+ (BOOL)supportsFeatureB;
}
... later ...
if ([[host plugInClass] supportsFeatureA]) {
... expose UI element ...
}
Rather than having to make plugin creation cheap so that I can do this:
class Host {
public Type PlugInClass {
get { ... }
set { ... }
}
}
class PlugIn {
public bool Host { get; }
public abstract bool SupportsFeatureB { get; }
public abstract bool SupportsFeatureA { get; }
}
... later ...
PlugIn plugIn = (PlugIn) Activator.CreateInstance(host.PlugInClass);
if (plugIn.SupportsFeatureA) {
... expose UI element ...
}
Another alternative is to introduce a PlugInFactory. Which might be
better in complex scenarios. But it's often overkill. And it's certainly
more typing.
In C#, I might also use attributes to decorate the plugin Type. But
those, too, are more typing than class methods (and also weakly typed).
Also, of course, inheriting constructors is nice. Of course, it opens up
the whole "designated constructor" can of worms. But that's okay; if
you're subclassing, it's your responsibility to make it work....
--
Gordon Henriksen
mali...@mac.com
We definitely have two instances of A since, B.isa(::A). We also have
a fragile implementation of count.
class A {
our %.count_of
method count (Class $c:) { %.count_of{$c} }
method BUILD {
$class = ($?SELF.class)
@countable_ancestors = $class.ancestors.uniq.grep :{.isa(::A)}
for $class, *@countable_ancestors -> $c { %.count_of{$c}++ }
}
Where we're assuming I've got the syntax of 'for' right, and that
'ancestors' is a class method that returns all of a class's
ancestors. This might not work too well in the face of a dynamic
inheritance tree, but it should be possible to work around. Something
like this might work:
Class A {
our %.instance_count_of
method count (Class $c: ?$with_descendents = undef) {
my @interesting_classes = $c;
if $with_descendents {
push @interesting_classes, *($c.all_subclasses);
}
[+] %.instance_count_of(@interesting_classes)
}
method BUILD {
%.instance_count_of($?SELF.class)
}
}
Where we're assuming that a class can find all its subclasses
--
Piers Cawley <pdca...@bofh.org.uk>
http://www.bofh.org.uk/
On Oct 11, 2005, at 9:10 PM, Gordon Henriksen wrote:
> On Tue, Oct 11, 2005 at 06:10:41PM -0400, Stevan Little wrote:
>> I would like to propose that class methods do not get inherited along
>> normal class lines.
>
> You mean, make them *not methods?* Because it's not a method unless it
> has an invocant, as far as I'm concerned. (Method implies
> polymorphism.)
No, they would still have an invocant. That invocant would be an anon-
class which itself is an instance of Class. It works like so:
(First, lets make a legend)
--> is instance of
..> is subclass of
NOTE: Class means the class named "Class", this distinction is
important.
When you create the class Foo, this is what you have:
Class
^
|
Foo
Foo is an instance of class Class (Class itself is an instance of
class Class too, but thats only slightly relevant here).
When you add a class method (one which cannot be inherited), it is
done with an eigenclass. This changes the above structure into this:
Class
^
:
eFoo
^
|
Foo
Now, we have created an anon-class (or eigenclass), whose entire
purpose is to hold the class methods of Foo. Since the eigenclass is
a subclass of Class, then all of Class's methods are inherited. This
means that our method dispatcher does not need to know about class
methods as a special case, as far as it is concerned, they are just
normal instance methods on Foo (which itself is an instance of eFoo,
which is then a subclass of Class).
Now, why are they not inherited. Lets expand this diagram a little more:
Class
^
+-----|----+
| |
Foo<.......Bar
So Foo and Bar are both instances of Class, and Bar is a subclass of
Foo. It is fairly straightforward, but now lets introduce the
eigenclasses to hold class methods.
Class
^
......:.....
: :
eFoo eBar
^ ^
| |
Foo<.......Bar
Now, method dispatch for Foo will go to eFoo (since Foo is an
instance of eFoo, and method dispatch always starts at the class-of
the instance), and it will continue up to Class (since Class is the
superclass of eFoo). The same goes for Bar, first to eBar, then to
Class. Since eFoo and eBar are not connected, then normal method
dispatching does not go along those lines.
Now, this is not to say that it cannot be made to do so. A slight
change to the above diagram allows for inheritence of "class methods"
very easily.
Class
^
:
eFoo<.......eBar
^ ^
| |
Foo<.......Bar
Now, method dispatch for Bar will go first to it's class (eBar), then
to any superclasses (eFoo), and any of their superclasses (Class),
and so on, and so forth. A better diagram of this can be found here
(http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/docs/
Method_Dispatch_w_EigenClasses.jpg).
> : Let's start by making a very basic definition of an *object*,
> : ignoring any implementation details or specifics.
> :
> : object == state + behavior
>
> I don't see how this is a bad thing. Classes don't have state. That's
> their lot in life. Que sera! Inheritance of behavior alone is useful.
Classes do have state though. They have class attributes:
class Foo {
our $.bar;
my $.baz;
}
that state is just not inherited.
I am not actually arguing that inheritance of just behavior is not
useful, more that inheritance of behavior *without the accompanying
state* is not useful, and in many cases wrong.
>
> My primary want for class methods, as a whole, is to provide this sort
> of interface in Objective-C:
>
> @interface Host : NSObject {
> - (Class)plugInClass;
> - (void)setPlugInClass:(Class) plugInClass;
> }
>
> @interface PlugIn : NSObject {
> - (BOOL)initWithHost:(Host *)host;
> + (BOOL)supportsFeatureA;
> + (BOOL)supportsFeatureB;
> }
>
> ... later ...
> if ([[host plugInClass] supportsFeatureA]) {
> ... expose UI element ...
> }
My Objective-C is very rusty, but let met see if I understand what
you are doing.
Host has-a Class object, which it uses as a plugInClass. Your PlugIn
then has class methods (supportsFeatureA, supportsFeatureB) which can
be used by the Host to query the capabilities of its plugInClass.
This type of thing could be accomplished with Roles.
class Host {
my $.plugInClass;
}
role PlugIn {
method initWithHost (Host $h:) { ... }
}
role SupportsFeatureA {
# yes, this Role has a "class method" in it, which
# the consuming class will get as a "class method"
method supportsFeatureA (Class $c:) { bool::true }
}
role SupportsFeatureB {
method supportsFeatureB (Class $c:) { bool::true }
}
class AB {
does PlugIn;
does SupportsFeatureA;
does SupportsFeatureB;
}
One could argue that it is more typing, however, I think that in the
long run, it will be less typing since you never need to repeat the
supportsFeatureA or supportsFeatureB method, just consume the
appropriate Role. As for subclassing of PlugIn's, that does get a
little trickier, however if Roles and Classes are interchangable
(which is not really specced that way in A12, but the current meta-
model prototype does support), then we can do this:
role SupportsFeatureC {
method supportsFeatureC (Class $c:) { bool::true }
}
class ABC {
does AB;
does SupportsFeatureC;
}
This basically consumes AB as if it was a Role, rather than
subclassing it, and the resulting ABC class should just DWIM (I would
really have to test this to be sure, but I think the idea is sound).
This system does not require inherited class methods, cheaply
initialized PlugIn classes, or a Factory pattern. Using Roles and
role composition I think we get an relatively simple and very
flexible system.
> Also, of course, inheriting constructors is nice. Of course, it
> opens up
> the whole "designated constructor" can of worms. But that's okay; if
> you're subclassing, it's your responsibility to make it work....
If you use the BUILD submethod, then you never need to worry about a
that, everything is initialized for you by BUILDALL. Now, if you want
to have a constructor which accepts positional arguments rather than
named pairs (as the default does), then you have a valid need to
override &new. Whether you should force this upon all your subclasses
is a matter of opinion I think.
Stevan
On Oct 12, 2005, at 5:22 AM, Piers Cawley wrote:
> We definitely have two instances of A since, B.isa(::A). We also have
> a fragile implementation of count.
:)
Sorry, I purposefully made it a kludge as that is usually the way the
example is shown in most tutorials about class methods.
>
> class A {
> our %.count_of
>
> method count (Class $c:) { %.count_of{$c} }
>
> method BUILD {
> $class = ($?SELF.class)
>
> @countable_ancestors = $class.ancestors.uniq.grep :{.isa(::A)}
You can use the MRO here, which is an already linearized list of the
inheritance tree (in C3 order) with all duplicates removed.
It just occurred to me that the system shown below could be re-
written to do away with class methods entirely.
class Host {
my $.plugInClass;
}
role PlugIn {
method initWithHost (Host $h:) { ... }
}
role FeatureA {}
role FeatureB {}
role FeatureC {}
class AB {
does PlugIn;
does FeatureA;
does FeatureB;
}
class ABC {
does AB;
does FeatureC;
}
Now later on, instead of asking the PlugIn if it "supportsFeatureB",
you can just see if it does the "FeatureB" role, like this:
if ($host.plugInClass.does('FeatureB')) {
# ... do something with FeatureB
}
This will work with the ABC plugin as well since AB is being treated
as a role, ABC will actually consume all it's subroles, which means
that ABC will DWIM too. In fact, we get even more from this system
since we can check if one plug-in is capable of "doing" another,
because this "just works"
if ($host.plugInClass.does('AB')) {
# ...
}
And since an example is better when it is backed up by working code,
I coded this up using the current meta-model prototype. You can see
it here: http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/t/
38_PlugIn_example.t
Stevan
On Oct 12, 2005, at 9:41 AM, Stevan Little wrote:
> class Host {
> my $.plugInClass;
> }
>
> role PlugIn {
> method initWithHost (Host $h:) { ... }
> }
>
> role SupportsFeatureA {
> # yes, this Role has a "class method" in it, which
> # the consuming class will get as a "class method"
> method supportsFeatureA (Class $c:) { bool::true }
> }
>
> role SupportsFeatureB {
> method supportsFeatureB (Class $c:) { bool::true }
> }
>
> class AB {
> does PlugIn;
> does SupportsFeatureA;
> does SupportsFeatureB;
> }
>
On Oct 12, 2005, at 11:04 AM, Gordon Henriksen wrote:
> On Oct 12, 2005, at 09:41, Stevan Little wrote:
>
>> If you use the BUILD submethod, then you never need to worry about
>> a that, everything is initialized for you by BUILDALL. Now, if you
>> want to have a constructor which accepts positional arguments
>> rather than named pairs (as the default does), then you have a
>> valid need to override &new. Whether you should force this upon
>> all your subclasses is a matter of opinion I think.
>
> For varying definitions of initialized. I never much cared for the
> bare "poke stuff straight into my instance variables" constructor
> along the lines of:
>
> sub new {
> my($class, %ARGS);
> return bless \%ARGS, $class;
> }
Yes, that is a horrible idiom which I hope will die in Perl 6.
> That more or less robs the constructor of the "behavior" part of
> "class = state + behavior." I need an opportunity to establish my
> invariants.
Well that is where BUILD comes in, you can do all sorts of mangling
of parameters in BUILD so that it does what you want it to, there is
no real need to put this behavior into &new.
> Of course, when there is no such behavior, it saves a lot of
> repetitive typing in the class. C# 3 is finally growing a syntax
> that resolves this by having the language do the repetitive typing
> at the call site...
>
> X x = new X{ Y = 1, Z = 2 };
>
> means
>
> X x = new X();
> x.Y = 1;
> x.Z = 2;
>
> And X doesn't need anything but the default constructor.
Yes, this is exactly what the &new -> CREATE -> BUILDALL -> BUILD
chain is doing too.
>> Now, this is not to say that it cannot be made to do so. A slight
>> change to the above diagram allows for inheritence of "class
>> methods" very easily.
>>
>> Class
>> ^
>> :
>> eFoo<.......eBar
>> ^ ^
>> | |
>> Foo<.......Bar
>>
>> Now, method dispatch for Bar will go first to it's class (eBar),
>> then to any superclasses (eFoo), and any of their superclasses
>> (Class), and so on, and so forth. A better diagram of this can be
>> found here (http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/
>> docs/Method_Dispatch_w_EigenClasses.jpg).
>
> This is more or less how class methods have to work. I would go a
> bit further, though. Too implement this:
>
> Foo : Object
> Foo : Bar
>
> The runtime should use an inheritance tree as such:
>
> Object
> Class : Object
> Foo : Object
> Bar : Foo
>
> _Object : Class
> _Class : _Object
> _Foo : _Class
> _Bar : _Foo
>
> Note that every declared class, including Object and Class
> themselves, have an anonymous Class subclass that precisely
> parallels the declared inheritance chain. (Chicken and egg problem?
> Probably. Object and Class are Special.)
This is pretty much the same thing that I am describing, except that
I don't think that Class needs an anon-class/eigenclass. All object
models need a cycle at the top, this keeps the "turtles-all-the-way-
down" problem away. The usual place is to put the cycle in Class
(Class is an instance of Class). If you add that anon-class, you
break the cycle.
> With this implementation, there are three places to put state: In
> MyObject (instance variable), in _MyObject (class instance
> variable), or outside of any instance (class variable). The class
> instance variable is the least useful of the three.
Well, I would argue that class instance variables are very useful,
since that is where methods and attribute meta-objects are stored.
But I think users should not have direct access to class instance
variables. But yes, other than that you are correct.
> Note: I don't see much value in invoking class methods through
> instances, since a Foo IS_NOT_A Class. If one wants to save the
> user typing ".class" when invoking class methods through an instance,
Yes, that is bad, another nasty p5 idiom I hope will go away.
> I would tend toward resolving it as such:
>
> class Foo {
> class_method int Bar(int i) { return i * i; }
> }
>
> -- BECOMES --
>
> # Common interface for Foo's class methods.
> interface _IFoo {
> method int Bar(int i);
> }
>
> # The anonymous Class.
> class _Foo extends Class implements _IFoo {
> int Bar(int i) { return i * i; }
> }
>
> # The visible class.
> class Foo implements _IFoo {
> # Forwards the call to the Class.
> void Bar(...) { return this.Class.Bar(...); }
> }
I think we can leave the interface part out, but yes, this is
basically how the eigenclasses work :)
> I'll leave the probably obvious role-based interpretation of this
> to those versed in such. :)
s/interface/role/ and you have the role based version ;)
Stevan
On Oct 11, 2005, at 8:47 PM, Larry Wall wrote:
> On Tue, Oct 11, 2005 at 06:10:41PM -0400, Stevan Little wrote:
> : Hello all.
> :
> : I would like to propose that class methods do not get inherited
> along
> : normal class lines.
>
> I think most class methods should be written as submethods instead.
In which case they would not be inherited then. I (obviously) agree
with you on that :)
> You seem to be arguing that a class has no state, but my view is that,
> in the abstract, a class encompasses the state of *all* its objects.
> It just hasn't picked one particular object to be at the moment.
No, not that class has no state, but that with the currently specced
classes we have inherited behaviors (class methods) but they do not
inherit the accompanying state (class attributes) as well. I see this
as potentially very problematic.
> : == Instance Counting Class
> :
> : The most common example given for class methods is an "instance
> : counter". Here is how one might (naively) look in Perl 6:
> :
> : class A {
> : our $.count;
> : method count (Class $c:) { $.count; }
> : submethod BUILD {
> : $.count++;
> : }
> : }
>
> That's obviously broken--the count accessor should be a submethod
> to be
> consistent, unless the explicit intent is that any subclass of A
> return the count of A's. Which, not surprisingly, is exactly what
> you get below. It should probably have been declared:
>
> our $.A_count;
>
> in that case. And in which case you don't need the explicit accessor,
> since one would have been provided because of the dot. If you don't
> want the autoaccessor, don't use the dot.
Yes, this example was purposefully broken, but also copied from
several tutorial on "how to use class methods".
>
> I suppose an argument could be made that autoaccessors for class vars
> should be submethods by default. Or maybe "my $.count" makes a
> submethod,
> while "our $.A_count" makes a method.
I think that is probably not a bad idea. If we not going to inherit
the class state, then we should not inherit the class method either.
>> : Clearly, we only have one instance of A, and one instance of B, so
>> : those numbers are wrong. It could be argued that since B is a
>> subtype
>> : of A, we do have two A's, but the argument does not work in
>> reverse.
>>
>> Sure it does. It doesn't matter whether B is a subtype of A or not,
>> you've given it an interface to code that counts A's.
True, that was probably a bad example (culled from other bad examples).
>> : But either way, I would argue that the results shown above are
>> : misleading, and probably not what the programmer intended.
>>
>> That's certainly possible, but it wouldn't be the first time people
>> have been surprised that the computer did exactly what they asked it
>> to... :-)
Also very true.
>> : Sure, you could do that, however, it complicates the meta-model
>> : unnecessarily. It is much easier to accomplish this using a
>> subclass
>> : of Class.
>> :
>> : class CountingClass is Class {
>> : has $.counter;
>> : method count (CountingClass $c:) { $.counter; }
>> : method new (CountingClass $c: %params) {
>> : $.counter++;
>> : next;
>> : }
>> : }
>> :
>> : class A meta CountingClass {}
>> : class B meta CountingClass {}
>> :
>> : Now A and B both have their own counters neither of which interfere
>> : with one another.
>>
>> Only by forcing people to repeat themselves as a matter of policy.
>> And policy could just as easily have added "our $.count" to B. The
>> real trick would be to set up A such that you don't have to do
>> anything
>> special in B. I suppose we could say that, by default, if A isa B,
>> then A gets also gets whatever metaclass B has, not the standard
>> metaclass supplied by "class".
I considered this as well, it seems to make some sense that the
metaclass of A is also the metaclass of B if A isa B. This would
actually simplify a particular edge case with the eigenclasses that
was problematic.
This diagram assumes we have inheritable class methods, and they are
implemented using the eigenclasses.
Class
^
:
eFoo<.......eBar
^ ^
| |
Foo<.......Bar
The method dispatch for Bar will go first to it's class (eBar), then
to any superclasses (eFoo), and any of their superclasses (Class),
and so on, and so forth. When you introduce a custom metaclass like so:
Class
^
:
CustomMeta
^
:
eFoo<.......eBar
^ ^
| |
Foo<.......Bar
A "problem" occurs with method dispatch for Bar. First it will go to
eBar, then to eFoo, then to CustomMeta, then to Class, etc, etc.
Since Bar was not explicitly created with CustomMeta, this is not
correct.
However, if the metaclasses act as you describe, this is then does
exactly what it is supposed to. I think it is a sane approach
personally.
>> But these are metaclasses, not classes. You keep writing the type of
>> the invocant of class methods as Class, but I don't believe that
>> anymore.
Sorry, just following the A12 examples :)
>> The type of the invocant of a class method in A is A, not Class. It
>> just happens to be a generic A, not a specific A, and tests as
>> undefined.
But the "type" of A is a Class correct?
If so, then this becomes a special case in the parameter parsing
which can take an instance rather than just the "type-of" an instance.
If we follow this more, then do we end up with things like Prolog/ML/
Erlang style pattern matching?
multi length([]) { 0 }
multi length(List @l) { 1 + length(@l.tail) }
Where params can be either (type => name) pairs, or some kind of
prototypical instance which we try to match against.
So then the class methods would actually look like this maybe:
method my_class_method (::A) { ... }
>> : Of course the "meta" syntax there is speculative,
>> : but surely you can accomplish that behavior somehow. This approach
>> : actually uses no class methods, only instance methods.
>>
>> You can already do this sort of thing using the trait syntax.
>> That's why I go on about "traitorous traits" in A12: "is foo" can
>> do *anything* to the container on its left, including installing
>> new parent classes and metaclasses. (But if I did want to define an
>> important new default-overriding metaclass, I'd probably replace the
>> "class" keyword with a word like "role", or "union", or "theory",
>> or whatever I thought was appropriate. Not sure counters rise to
>> that level though.)
I thing that would maybe be overkill for specificing custom
metaclasses. Wouldn't it suffer from the same "repeating yourself"
policy issues you described above?
>> : NOTE: I am assuming that the Countable role will mix-in the class
>> : method &count as well as the class attribute $.count. And that
>> "A but
>> : Countable" is really sugar for something like "class { does
>> : Countable; is A }".
>>
>> Well, hey, we're out ahead of the state of the art here, so we can
>> do whatever we like with roles.
At this point, I assume that anything and everything inside a role is
consumed by the class, so that the role can be then be disposable
(unless of course another class wants to use it).
>>
>> : == Custom Constructors
>> :
>> : Another common example of class method usage is custom
>> constructors.
>> : This example is moot given the BUILDALL/BUILD system. All class
>> : specific initialization can easily be done using custom BUILD
>> : submethods.
>>
>> The purpose of custom constructors in Perl 6 is to allow the use of
>> positional args. I can't think of any other use for them, offhand...
That's all I got too. And I am not sure you would always want to that
be inherited either.
>> : In Smalltalk, "new" is
>> : actually an instance method of the class Class, and it calls the
>> : specific object's "new" instance method to initialize (somewhat
>> like
>> : the CREATE->BUILDALL/BUILD in Perl 6). In the Perl6-MetaModel
>> : prototype, &new is implemented as an instance method of Class, and
>> : not a class method of Object (as is sometimes assumed).
>>
>> I think that, at least as a name, the default new() *should* be in
>> Object, though of course most of its work might done by Object.meta
>> instead.
So &new in Object a class method? Why? It works just fine as an
instance method of Class.
>> I don't believe in Class as a class anymore, unless that's
>> what .meta reaches these days.
What would Class be if it is not itself an instance of Class?
>> If you want to change the behavior
>> of a class, you're really talking about changing the behavior of
>> the metaclass instance (which typically stores various bits of its
>> data in a package). The A class is just a proxy for all that, so
>> that you can reason about objects of type A without actually having
>> one.
Having an instance of Class which describes A allows you to reason
about A very easily without needing an instance of A. I think maybe
the ways in which you reason about it might be different, but there
is no reason you cannot do so. A Class instance should respond
to .isa and .does exactly the same way an instance of said class
would. The rest can be done with .meta introspection on superclasses,
methods and attributes. Classes are just more complex beasts than
instances, I think a reflection API/Meta Object Protocol will give
you all that you need and more.
>> I'm beginning to suspect Class is just a mixin to that breaks the
>> instance association, such that if you did
>>
>> $fido = new Dog;
>> $fido does Class;
>>
>> you'd end up with $fido undefined (but still of type Dog).
I am not sure that code makes sense. How would that be useful?
Usefulness aside, why do Roles and Classes need to be seperate
beasts? In the current meta-model prototype, the role system is laid
atop the class system so that the following is true:
Class is an instance of Class
Role is an instance of Class
Class does Role
This then means that Role also .does Role since Role is an instance
of Class (which does Role).
It gets very cyclical, but it essentially means that all classes can
be treated as roles. This allows for all sorts of interesting things
to happen actually.
I have to admit though, that this comes directly from Scala (so maybe
we are not alone here out on the edge :)
>> So basically, you declare your constructor
>>
>> method new (Class $c: ...)
>>
>> if you want to restrict $c to something that does the Class
>> *role*, which
>> guarantees there's no associated instance.
I think this could be accomplished by just saying "parameter checking
checks .does() and not .isa()" (which would make chromatic happy).
But I am not sure there is a need to make Class a role.
>> But if you're more into prototyping, you'd say
>>
>> method modified (Dog $d: ...)
>>
>> if you want to restrict $d to something that does Dog, and you don't
>> care whether it happens to have an instance associated because the
>> class already defines decent defaults for any attributes you don't
>> override.
I think this would work as well if parameters checked .does() since
the Dog class surely .does(Dog).
>> I want Perl 6 to encompass both class-based and prototype-based
>> approaches, and this seems like the right way to do it.
I think given enough meta-accessibility, a model whose core is class-
based can be easily used as prototype-based.
>> I think there's no such thing as a Class instance. That simplifies
>> things even further.
Do you mean no instance of Class called "Class"? or no instances of
Class at all? If so, how would we represent classes?
Stevan
On Oct 12, 2005, at 10:48 AM, Gordon Henriksen wrote:
> Actually, I wondered why you didn't suggest this earlier. :) I
> figured you were a step ahead of me: What if I want more than a
> boolean out of my class method?
Then you put the class methods back in :)
But then your Objective-C interface would need to change too.
Although, the more complexity you introduce, the closer you get to
the point when a Factory pattern is just as viable an approach as
class methods.
Stevan
> —
>
> Gordon Henriksen
> gordonh...@mac.com
>
>
>
>
> If you use the BUILD submethod, then you never need to worry about
> a that, everything is initialized for you by BUILDALL. Now, if you
> want to have a constructor which accepts positional arguments
> rather than named pairs (as the default does), then you have a
> valid need to override &new. Whether you should force this upon all
> your subclasses is a matter of opinion I think.
For varying definitions of initialized. I never much cared for the
bare "poke stuff straight into my instance variables" constructor
along the lines of:
sub new {
my($class, %ARGS);
return bless \%ARGS, $class;
}
That more or less robs the constructor of the "behavior" part of
"class = state + behavior." I need an opportunity to establish my
invariants.
Of course, when there is no such behavior, it saves a lot of
repetitive typing in the class. C# 3 is finally growing a syntax that
resolves this by having the language do the repetitive typing at the
call site...
X x = new X{ Y = 1, Z = 2 };
means
X x = new X();
x.Y = 1;
x.Z = 2;
And X doesn't need anything but the default constructor.
> Now, this is not to say that it cannot be made to do so. A slight
> change to the above diagram allows for inheritence of "class
> methods" very easily.
>
> Class
> ^
> :
> eFoo<.......eBar
> ^ ^
> | |
> Foo<.......Bar
>
> Now, method dispatch for Bar will go first to it's class (eBar),
> then to any superclasses (eFoo), and any of their superclasses
> (Class), and so on, and so forth. A better diagram of this can be
> found here (http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/
> docs/Method_Dispatch_w_EigenClasses.jpg).
This is more or less how class methods have to work. I would go a bit
further, though. Too implement this:
Foo : Object
Foo : Bar
The runtime should use an inheritance tree as such:
Object
Class : Object
Foo : Object
Bar : Foo
_Object : Class
_Class : _Object
_Foo : _Class
_Bar : _Foo
Note that every declared class, including Object and Class
themselves, have an anonymous Class subclass that precisely parallels
the declared inheritance chain. (Chicken and egg problem? Probably.
Object and Class are Special.)
With this implementation, there are three places to put state: In
MyObject (instance variable), in _MyObject (class instance variable),
or outside of any instance (class variable). The class instance
variable is the least useful of the three.
Note: I don't see much value in invoking class methods through
instances, since a Foo IS_NOT_A Class. If one wants to save the user
typing ".class" when invoking class methods through an instance, I
would tend toward resolving it as such:
class Foo {
class_method int Bar(int i) { return i * i; }
}
-- BECOMES --
# Common interface for Foo's class methods.
interface _IFoo {
method int Bar(int i);
}
# The anonymous Class.
class _Foo extends Class implements _IFoo {
int Bar(int i) { return i * i; }
}
# The visible class.
class Foo implements _IFoo {
# Forwards the call to the Class.
void Bar(...) { return this.Class.Bar(...); }
}
I'll leave the probably obvious role-based interpretation of this to
those versed in such. :)
And I'm going to shut my yap, now, having butted into the middle of a
discussion of a hopelessly complex runtime that I haven't been
following for a 18 months. :)
—
Gordon Henriksen
gordonh...@mac.com
> Usefulness aside, why do Roles and Classes need to be seperate
> beasts? In the current meta-model prototype, the role system is laid
> atop the class system so that the following is true:
>
> Class is an instance of Class
> Role is an instance of Class
> Class does Role
>
> This then means that Role also .does Role since Role is an instance
> of Class (which does Role).
>
> It gets very cyclical, but it essentially means that all classes can
> be treated as roles. This allows for all sorts of interesting things
> to happen actually.
I've always thought that classes were more specific than roles in that
you can't apply a class to another class. (Maybe that's the wrong
direction of specificity. Though I am positive that .does() is more
general than .isa(), I always have to stop and think about which
direction co- and contra-variance goes.)
Certainly I think declaring a class should imply a role of the same
name, even if you can't actually apply that role and mix in state and
behavior.
> I have to admit though, that this comes directly from Scala (so maybe
> we are not alone here out on the edge :)
I've also heard that Sather does something similar but don't know any
details.
-- c
—
Gordon Henriksen
gordonh...@mac.com
I think you're not thinking about many major usage cases for class methods.
For one example, look at my Cipher suite. (It's in Pugs's ext/Cipher
directory.) The Cipher base class implements most of the visible API,
while subclasses simply override a few internal methods; Cipher turns
the wide-ranging, convenient external API into a smaller, more easily
implementable internal API.
Some of Cipher's methods are class methods, including the
pseudo-procedural .encipher/.decipher and the pseudo-functional
.encipherer/.decipherer methods. These methods are included
specifically *to* be inherited.
In my opinion, class method inheritance is an important part of
class-based OO--almost as important as object method inheritance.
Removing features simply because their implementation is inconvenient
is not The Perl Way. If it were, Perl 6 would be Java With Sigils.
--
Brent 'Dax' Royal-Gordon <br...@brentdax.com>
Perl and Parrot hacker
I'm partly to blame for this thread because I put the idea into
Steve's head that class methods being inheritable may be dogma and not
a useful thing. Mea culpa.
That said, I want to put forward a possible reason why you would
want class methods to be inheritable - to provide pure functions.
If we go back to every function/method/subroutine being behavior,
it's pretty evident that you need some state for that behavior to
operate upon. In a procedural function, the state is provided. In an
object, that state is both provided and inherent in the instance the
method is operating upon. In functional languages, functions maintain
their own state through closures, continuations, and the like.
I might want to provide in a base class a set of useful sorting
routines or other such items. These would be true procedural functions
and there would be a warning thrown if it accessed class state.
Rob
On Oct 11, 2005, at 8:17 PM, Brent 'Dax' Royal-Gordon wrote:
> Stevan Little <ste...@iinteractive.com> wrote:
>
>> I would like to propose that class methods do not get inherited along
>> normal class lines.
>>
>
> I think you're not thinking about many major usage cases for class
> methods.
Actually I have considered many common usages including those which
you describe below, and it is my belief that only a few are truly
valid uses and not abuses of class method functionality. What I kept
coming back to was that pretty much all the *abuses* of class methods
were better done in some other way, and that even the *valid* uses
were nothing more than design choices, and could be accomplished in
some other manner.
> For one example, look at my Cipher suite. (It's in Pugs's ext/Cipher
> directory.) The Cipher base class implements most of the visible API,
> while subclasses simply override a few internal methods; Cipher turns
> the wide-ranging, convenient external API into a smaller, more easily
> implementable internal API.
Your internal API and your external API have little to do with one
another as far as I can tell. The external API is simply a set of
convenience functions which create instances of your classes in
various ways (very cool ways I might add, especially the functional
API, very nice stuff). However, you could easily remove external API,
and your internal API would not really suffer, it would only require
that the user manually create what your class methods create for you.
While many people think Factories are many times overkill (me among
them), what you are doing is a perfect candidate for the Factory
pattern. In fact, one could say you are already doing an ad-hoc
Factory pattern with your inheritable class methods.
> Some of Cipher's methods are class methods, including the
> pseudo-procedural .encipher/.decipher and the pseudo-functional
> .encipherer/.decipherer methods. These methods are included
> specifically *to* be inherited.
Your documentation says the following things:
The Cipher API's procedural interface is good enough for many
purposes.
Although the interface is said to be procedural, it is invoked via
two class
methods.
The Cipher API is fundamentally object-oriented; the procedural
and functional
interfaces are layers on top of the object-oriented backend.
Both indicate to me an acknowledgment that you are knowingly abusing
the inheritance of class methods to make your functional and
procedural APIs work. Now, please don't take this as an insult or
slam of some kind. All good programmers know when to abuse language
elements to get what they need. However, I am of the opinion that
maybe we should leave these old idioms/abuses aside.
> In my opinion, class method inheritance is an important part of
> class-based OO--almost as important as object method inheritance.
I disagree with you on this point (of course, otherwise I would not
have started this thread), but I will admit that inheritable class
methods are a very common OO idiom, and that fact (good or bad)
should be taken into account.
> Removing features simply because their implementation is inconvenient
> is not The Perl Way. If it were, Perl 6 would be Java With Sigils.
To be honest, the implementation is not inconvenient at all, in fact
I have already done it twice (correctly at least, the meta-model
currently has inheritable class methods, but the implementation is
crap).
1) A Mini-MetaModel with Eigenclasses
http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/docs/
MiniMetaModel_w_eigenclasses.pl
Whenever I am doing something which has the potential to dig deeply
into the core of the meta-model, I do it first with a Mini-MetaModel.
(The MMM (mini-meta-model) is a small self-bootstrapping single-
inheritance meta-model in under 2-300 LOC and usually which tends to
be much easier to "mess with" than the real meta-model.) If you look
at the "new" method in Class, you will see it creates an Eigenclass
for each class (this is where the class methods get stored), then
adding class methods is accomplished with the "add_singleton_method"
method. You will find a number of tests towards the bottom of the
file which demonstrate the inheritance of the class methods.
2) By using a subclass of Class
http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/t/
37_inherited_class_methods.t
I did this test at autrijus's request, it creates a
"ClassWithInheritedClassMethods" class which is a subclass of Class.
If you create your classes (Foo, Bar, what have you) using
"ClassWithInheritedClassMethods", then you can add class methods,
again with the "add_singleton_method", and they are inherited
correctly by subclasses.
The code to make "ClassWithInheritedClassMethods" work is only 10
lines long, so as you can see the implementation is not difficult or
inconvenient at all.
To properly implement this in the current meta-model prototype would
not require all that much more effort than either of these examples.
Now, all this said, I think you make a very important point, which is
that such B&D does conflict with "The Perl Way". And that it probably
will get in the way of "getting the job done" which is just contrary
to what Perl is all about.
Stevan
Stevan Little wrote:
> On Oct 11, 2005, at 8:47 PM, Larry Wall wrote:
>> You seem to be arguing that a class has no state, but my view is that,
>> in the abstract, a class encompasses the state of *all* its objects.
>> It just hasn't picked one particular object to be at the moment.
I love this notion because that makes the type that the class represents
the potential state it would reach when *all* distinguishable instances
were realized---which needs a lot of memory for many classes. But the
bit class/type e.g. has exactly two distinguishable states! Well and
more then these two will never be needed if the algebraic, boolean type
is build with !, && and ||. Negation just exchanges the half of the
type that is in use, the former one is returned into potentiality,
that is, it leaves the programs actuality. The binary connectives leave
false and true in the program respectively as soon as both booleans have
entered the program! That is (1 && 0 == 0) and (0 || 1 == 1) dual each
other, just as the unrealized potential state of a class duals the actually
realized state of the instances of the class in any particular moment.
Here's a nice magic square picture of the idea:
-Class .. ::Class meta space
*--------*
| /|
| / | The diagonal line splits the potential(-) from
| / | the actual(+) instances. Undef and the ::Class
| / | are the "singularities" of the class. The state
| / | of the class moves up the diagonal through instance
| / | creation, while the GC moves it towards Undef.
| / |
*--------*
Undef .. +Class value space
> No, not that class has no state, but that with the currently specced
> classes we have inherited behaviors (class methods) but they do not
> inherit the accompanying state (class attributes) as well. I see this
> as potentially very problematic.
What do you mean with "not inheriting class state"? I would think that
beeing an instance of a class, an object should have access to the shared
class data, but it is not part of the per object state. Or do you mean
that class data is not accessable to the instances at all? I would hope
that this is 'hidden' in the scopes where the class's parts are defined
but of course accessable lexically from class support code there.
--
I mean that classes do not inherit class state along subclass lines.
Take this code for instance:
class Foo {
has $.bar;
}
every instance of Foo I create has it's own copy of $.bar. Now I
subclass it:
class Bar is Foo {}
every instance of Bar has it's own copy of $.bar as well. Now look at
this from the class attribute perspective.
class Foo {
our $.baz; # class data here,.. not instance data
}
Foo has a single copy of $.baz in it's internal namespace. Now
subclass it:
class Bar is Foo {}
Bar does not have it own copy of $.baz, in fact Bar's ability to
access Foo::{$.baz} (or however it would be spelt) is not more
special than any other class in the system.
The point I am trying to make is that class data is unique to the
class itself, and subclasses of said class do not have any special
priviledges or access to that data (leaving auto-generated accessors
out of the picture for now).
This is different though from how class methods behave, which seems
to me to be problematic.
Stevan
I propose that class methods are inheritable, but have the following
behaviors/attributes:
1) Autogenerated Class attribute accessors will be submethods.
This means that this code:
class Foo {
our $.bar;
}
will be functionally identical to this code:
class Foo {
our $.bar;
submethod bar (Class $c:) { $.bar }
}
This will ensure that class methods which are specifically tied to
some kind of state within the class will not be inherited. At least
not by default, if you want that behavior, then you can do this:
class Foo {
our $.bar;
method bar (Class $c:) { $.bar }
}
2) Class methods be defined more specifically
I think that "method (Class $c:) { ... }" is kind of ugly, and if we
use eigenclasses to implement class methods, is not even correct.
Ideally we have some kind of way to represent Larry's "Dog but undef"
concept. This could be thought of as being the prototypical instance
(for those who like prototype based OO), and it would also be the
invocant for all class methods for Dog.
So anyway, here are a few ideas, in no particular order:
method bark (::Dog $d:) { ... }
# not sure if this notation is already taken or not
method bark ($Dog $d:) { ... }
# not sure I like this one myself, but to me it helps to re-
enforce the singleton nature of the class instance
method bark (Dog) { ... }
# this would be similar to functional languages where the
parameter matches a value, not the type of a value.
# The user would then be forced to use $?CLASS inside (this one is
probably too much B&D)
classmethod bark { ... }
# you can't get more specific than this :)
Okay, thats all for now, however, be on the lookout for some other
mails on the specifics of class method dispatch. If we are going to
do it, we need to do it right.
Thanks,
Stevan
How about:
method bark (Dog ::K:) { ... }
Where ::K must hold a class that is a subclass of Dog. Or 'is a
subtype of', or 'does', or whatever the most correct term is in this
context.
(I seem to recall Damian mentioning something like this on the list
somewhere, but I might have misunderstood him and made it up myself.)
I do notice, though, that most of these class-method forms don't stand
out very well--maybe something like 'classmethod' might be best after
all, particularly if class methods aren't heavily used.
Stuart
> Piers,
>
> On Oct 12, 2005, at 5:22 AM, Piers Cawley wrote:
>> We definitely have two instances of A since, B.isa(::A). We also have
>> a fragile implementation of count.
>
> :)
>
> Sorry, I purposefully made it a kludge as that is usually the way the example
> is shown in most tutorials about class methods.
So, let me see if I have this straight here. You're arguing that, because
people are often foolish, we should make it harder to be clever? And you're
using a deliberately broken example as grist to your mill?
Doesn't sound all that Perlish to me.
On Oct 14, 2005, at 12:14 PM, Piers Cawley wrote:
> Stevan Little <ste...@iinteractive.com> writes:
>> On Oct 12, 2005, at 5:22 AM, Piers Cawley wrote:
>>> We definitely have two instances of A since, B.isa(::A). We also
>>> have
>>> a fragile implementation of count.
>>>
>>
>> :)
>>
>> Sorry, I purposefully made it a kludge as that is usually the way
>> the example
>> is shown in most tutorials about class methods.
> So, let me see if I have this straight here. You're arguing that,
> because
> people are often foolish, we should make it harder to be clever?
No, more that people have been lead down the wrong paths all too
often based on limitations of language implementation. And that Perl
6 should be moving to correct this problem. Sure that might be un-
Perlish in the sense that we are not leaving every "way to do it"
open. But at some point I think you need to shake off the old
accumulated crud and start fresh, even if that new way might go
contrary to what some people have been conditioned to think.
I also do not believe that I am making it "harder", as much as I am
making it "different". Change is hard, but it *is* inevitable.
I think Perl 6's OO system has the potential to be to OO programming
what Perl 5, etc was to text processing. This, I believe, is in large
part due to the fact that Perl 5 had such a "slim" object system, and
Larry intended (based on what I have read in A12) to start fresh from
the ground up. That, coupled with Perl's tendency to "borrow" the
best from everywhere, and you have the potential for a really great
OO system.
> And you're using a deliberately broken example as grist to your mill?
The example is the canonical example for class methods/attributes,
and yes it is broken. However, it's broken-ness only serves to
illustrate, what I think is, the misunderstanding of the usefulness
of class methods in general. One only needs to take a quick sampling
of some of the more popular CPAN modules which sport an "OO"
interface to see that all to often class methods are (ab)used to get
what amounts to "procedural modules with inheritence".
> Doesn't sound all that Perlish to me.
Perl is many different things to different people, this is part of
it's beauty as a language. Perl also has the unique ability to be
able to re-invent itself on a regular basis (shell-scripts, CGI,
bioinformatics, what next??). I personally think that the definition
of what is "Perlish" and what is not "Perlish" is not only highly
subjective, but ever changing.
So I guess what I am saying here is "thank you", as I will take that
as a compliment ;)
Stevan
Look guys, I want it to just consistently be
method bark (Dog $d) {...}
regardless of how instantiated the dog is. Think of partially
instantiated subroutines via .assuming. A sub is a sub regardless of
how much it's been curried. So who cares if it's a complete Dog or
a partial Dog, or a completely generic Dog? Nobody cares until you
try to call a specific method that relies on some specific attribute.
If you call a sub with an incomplete definition, you should be prepared
to handle the exception. If you call a method with an incomplete object,
you should be prepared to handle the exception.
Of course, by that argument, $d should be considered defined even if
it's a completely uninstantiated class object. With subs the final
proof of actual well-definedness (not to be confused with .defined())
is whether it can be bound to a particular set of arguments. It's a
rather lazy definition of well-definedness. I'm proposing that
all objects follow the same model of not caring how well they're
defined until you actually try to use them for something. I don't
think I care any more about whether classes test as defined or not.
It's like reality--there are a lot of complex problems where the
simplest way to simulate them is via reality itself, and all other
simulations are guaranteed to be slower.
But we have to think a bit more about the notion of currying class
objects into real objects, or something approaching real objects.
This only reinforces my view that all the meta stuff for Dog must
be via the .meta or the associated package. There is no Class object.
It's a false dichotomy. Class is a role that manages partially
instantiated objects, just as Routine (or whatever it is these
days) is a role that manages partially instantiated sub calls.
And they mostly manage by delegation to .meta.
Larry
I have been giving a lot of thought to the way you have been
describing classes lately. I think I understand where you are going
with it, but I need to understand some of the details.
On Oct 14, 2005, at 2:15 PM, Larry Wall wrote:
> This only reinforces my view that all the meta stuff for Dog must
> be via the .meta or the associated package. There is no Class object.
> It's a false dichotomy. Class is a role that manages partially
> instantiated objects, just as Routine (or whatever it is these
> days) is a role that manages partially instantiated sub calls.
> And they mostly manage by delegation to .meta.
If I understand you correctly then, what I have been calling a class
object is just really the "thing" on the other end of .meta.
When I say "class object", I mean some kind of object instance which
contains all the information to describe a class (methods, meta-
attributes, superclass list, etc). This can just as easily be called
a metaclass instance too, it makes no difference to me.
As for the idea that "Class is a role that manages partially
instantiated objects", I am not sure i am understanding what you mean
here. I am assuming this is along the lines of the "Class's are a
special form of undef" idea. In that case I can see where maybe the
Class "thing" is simply a parameterized role of some kind which has
the following behaviors:
1) it evaluates to undef in an expression
my Dog $fido;
if ($fido) {
# this won't run, $fido is not yet defined
}
2) it acts as a proxy if a method is called on it
my Dog $fido;
$fido .= new();
If this is true, then maybe Class looks something like this:
role Class[MetaClassType $T] {
# evaluate to false in bool context
method prefix:? () { bool::false }
# probably need to overload some other
# operators here too, but I will leave
# that for now
# the AUTOMETH checks for an calls
# any method you attempt to call on
# this particular class
# ( I am not sure about the details
# of the code here, but you get the
# idea I think )
method AUTOMETH { $T.can($_).(*@_) }
}
Then if this were so, then the following:
my Dog $fido;
Would be de-sugared into:
my $fido = Class[Dog].new();
At least this is how I am seeing it currently in my head. Please let
me know if I am way off the mark here, I am trying to understand how
this all will work. Ideally it can fit into the current meta-model
prototype as designed, if not, I have no problem re-writing it again,
but I just need to wrap my head around how this should work.
Thanks much,
Stevan
On Oct 14, 2005, at 2:15 PM, Larry Wall wrote:
> Look guys, I want it to just consistently be
>
> method bark (Dog $d) {...}
>
> regardless of how instantiated the dog is. Think of partially
> instantiated subroutines via .assuming. A sub is a sub regardless of
> how much it's been curried. So who cares if it's a complete Dog or
> a partial Dog, or a completely generic Dog? Nobody cares until you
> try to call a specific method that relies on some specific attribute.
> If you call a sub with an incomplete definition, you should be
> prepared
> to handle the exception. If you call a method with an incomplete
> object,
> you should be prepared to handle the exception.
>
> Of course, by that argument, $d should be considered defined even if
> it's a completely uninstantiated class object. With subs the final
> proof of actual well-definedness (not to be confused with .defined())
> is whether it can be bound to a particular set of arguments. It's a
> rather lazy definition of well-definedness. I'm proposing that
> all objects follow the same model of not caring how well they're
> defined until you actually try to use them for something. I don't
> think I care any more about whether classes test as defined or not.
> It's like reality--there are a lot of complex problems where the
> simplest way to simulate them is via reality itself, and all other
> simulations are guaranteed to be slower.
I think what bothers me most about this is that it seems there is no
way to tell the difference between class methods and instance
methods. That the distinction is only made when the body of the
method does something which is is not supposed to do (method called
with a class invocant attempts to access an instance variable, etc).
This is one of the major problems that I have always had with Perl 5
OO. That there is a very fuzzy fuzzy line between the
responsibilities of a class and an instance.
I can see the notion of "a class which is not yet instantiated", this
makes sense in many contexts. But I don't think that in order to have
this, we need to bring back this element of Perl 5 OO. I think we can
still have all the behaviors you have been describing, and still keep
classes and their instances as distinct entities.
> But we have to think a bit more about the notion of currying class
> objects into real objects, or something approaching real objects.
This is an interesting thought, I will have to ponder it some, but it
has a nice smell. Of course I love indian food and functional
programming, so that may be personal bias :)
Stevan
But you haven't actually said why this is a problem. If you want to
know at compile time that a method must be called with an object that
has been instantiated, it seems to me that it's pretty easy to tell
99% of the time whether the method body has made reference to $?SELF
in some form or other. And if the compiler can determine that, why
should the user have to specify it?
: I can see the notion of "a class which is not yet instantiated", this
: makes sense in many contexts. But I don't think that in order to have
: this, we need to bring back this element of Perl 5 OO. I think we can
: still have all the behaviors you have been describing, and still keep
: classes and their instances as distinct entities.
Well sure, they're at least opposite ends of a continuum. But we
may usefully smudge the distinction in the middle unless you can show
actual damage from this. And as you pointed out in your other message,
your notion of class is mostly hidden behind .meta in my scheme of
things anyway. And generally the compiler will know by inspection
whether the body is referring to the dynamic class through .meta, the
static class through $?CLASS, or the dynamic instance through $?SELF.
(And yes, it bothers me that $?CLASS is static while $?SELF is dynamic.
The latter is an abuse of the $? sigil, which ought to be reserved
for entities known to the compiler. Maybe we should rename $?SELF
to something that looks more dynamic. $*SELF is dynamic, but implies
global. Hmm, if $.foo() is really a self call, maybe we could make a
case for self being $$. Of course, there's never been any controversy
here about what to call "self", oh no... :-)
: >But we have to think a bit more about the notion of currying class
: >objects into real objects, or something approaching real objects.
:
: This is an interesting thought, I will have to ponder it some, but it
: has a nice smell. Of course I love indian food and functional
: programming, so that may be personal bias :)
Could turn out that $obj.assuming is just a generalization of
&func.assuming. That'd be kinda cool.
Larry
IMHO just call it "self" (by default) and be done with it. :)
--
wolverian, contributing to the general disagreement
Let it be so.
Larry
On Oct 15, 2005, at 11:25 AM, Larry Wall wrote:
> : >But we have to think a bit more about the notion of currying class
> : >objects into real objects, or something approaching real objects.
> :
> : This is an interesting thought, I will have to ponder it some,
> but it
> : has a nice smell. Of course I love indian food and functional
> : programming, so that may be personal bias :)
>
> Could turn out that $obj.assuming is just a generalization of
> &func.assuming. That'd be kinda cool.
What would be in the other side of $obj.assuming?
Assuming $obj is an instance already, what are we creating with it?
Or perhaps you mean Object.assuming()?
In which case it would produce some partially instantiated class.
Please clarify :)
Stevan
On Oct 15, 2005, at 11:25 AM, Larry Wall wrote:
> On Sat, Oct 15, 2005 at 10:34:34AM -0400, Stevan Little wrote:
> : I think what bothers me most about this is that it seems there is no
> : way to tell the difference between class methods and instance
> : methods. That the distinction is only made when the body of the
> : method does something which is is not supposed to do (method called
> : with a class invocant attempts to access an instance variable, etc).
> :
> : This is one of the major problems that I have always had with Perl 5
> : OO. That there is a very fuzzy fuzzy line between the
> : responsibilities of a class and an instance.
>
> But you haven't actually said why this is a problem. If you want to
> know at compile time that a method must be called with an object that
> has been instantiated, it seems to me that it's pretty easy to tell
> 99% of the time whether the method body has made reference to $?SELF
> in some form or other. And if the compiler can determine that, why
> should the user have to specify it?
I think it is a problem because they are two distinct areas of
responsibility. A class creates and manages instances, while an
object is the thing which the class creates. The object only manages
it's own data.
As for whether the compiler can tell the difference, I am not sure
you are correct here. Take this for instance:
class Foo {
method bar {
$?CLASS.baz();
}
}
What is &bar? A class method? or a instance method? Clearly $?CLASS
is valid inside instance methods, and surely you can call methods on
$?CLASS within instance methods. There is an ambiguity here.
One possible solution of course is that this method is both, that it
could basically be this (to use the old A12 syntax):
class Foo {
method bar (Class|Foo $f:) {
$?CLASS.baz();
}
}
I am fine with that personally.
My biggest concern is that we are able to fit the syntax into *a*
meta-model. As I said before, I don't have a problem scrapping the
current version and starting on v3.0, I enjoy writing and thinking
about these things, so you will get no resistance from me. However, I
want to be sure that I know what it needs to do, then I can determine
if I even need to re-write it or not.
> : I can see the notion of "a class which is not yet instantiated",
> this
> : makes sense in many contexts. But I don't think that in order to
> have
> : this, we need to bring back this element of Perl 5 OO. I think we
> can
> : still have all the behaviors you have been describing, and still
> keep
> : classes and their instances as distinct entities.
>
> Well sure, they're at least opposite ends of a continuum. But we
> may usefully smudge the distinction in the middle unless you can show
> actual damage from this.
I don't think there is actually damage, we just have methods which
can be called in either context. How to implement this is fairly
easy, so I am not worried about that. I am just not comfortable with
*all* methods being in this grey area.
> And as you pointed out in your other message,
> your notion of class is mostly hidden behind .meta in my scheme of
> things anyway. And generally the compiler will know by inspection
> whether the body is referring to the dynamic class through .meta, the
> static class through $?CLASS, or the dynamic instance through $?SELF.
Yes, except in the case I show above, but then we can just use the
solution I show above.
> (And yes, it bothers me that $?CLASS is static while $?SELF is
> dynamic.
> The latter is an abuse of the $? sigil, which ought to be reserved
> for entities known to the compiler. Maybe we should rename $?SELF
> to something that looks more dynamic. $*SELF is dynamic, but implies
> global. Hmm, if $.foo() is really a self call, maybe we could make a
> case for self being $$. Of course, there's never been any controversy
> here about what to call "self", oh no... :-)
I don't even want to get into this debate, I am just the meta-monkey,
nothing more :)
Thanks,
Stevan
Somewhat off-tangent: does this mean that .foo is always $_.foo?
> Larry
--
Ilmari Vacklin
>
> I think what bothers me most about this is that it seems there is no
> way to tell the difference between class methods and instance
> methods. That the distinction is only made when the body of the
> method does something which is is not supposed to do (method called
> with a class invocant attempts to access an instance variable, etc).
>
> This is one of the major problems that I have always had with Perl 5
> OO. That there is a very fuzzy fuzzy line between the
> responsibilities of a class and an instance.
>
> I can see the notion of "a class which is not yet instantiated", this
> makes sense in many contexts. But I don't think that in order to have
> this, we need to bring back this element of Perl 5 OO. I think we can
> still have all the behaviors you have been describing, and still keep
> classes and their instances as distinct entities.
It just recently occured to me that Class is a Package. So, on the
object model level, class methods/attributes belong to the Package part
of a class, while instance methods/attributes belong to the Class part
of a class - insofar as they're made distinct by use of my/our.
Larry's proposal is to remove that difference for multimethods.
Personally I can't think of a good objection to that idea (except if it
may be bad for performance - is there a plan to infer types and
auto-inline? I that case, declaring that you don't care about instance
part of the object's functionality can really help).
Miro
On Oct 17, 2005, at 7:35 AM, Miroslav Silovic wrote:
> ste...@iinteractive.com wrote:
>> I think what bothers me most about this is that it seems there is
>> no way to tell the difference between class methods and instance
>> methods. That the distinction is only made when the body of the
>> method does something which is is not supposed to do (method
>> called with a class invocant attempts to access an instance
>> variable, etc).
>>
>> This is one of the major problems that I have always had with Perl
>> 5 OO. That there is a very fuzzy fuzzy line between the
>> responsibilities of a class and an instance.
>>
>> I can see the notion of "a class which is not yet instantiated",
>> this makes sense in many contexts. But I don't think that in
>> order to have this, we need to bring back this element of Perl 5
>> OO. I think we can still have all the behaviors you have been
>> describing, and still keep classes and their instances as
>> distinct entities.
>>
>
> It just recently occured to me that Class is a Package.
Actually, to be precise, Class is a Module, and Module is a Package.
Modules add the version and authority portions to the name of a
Package, and it seems that exporting (as traits?) are Module things,
and not Package things.
> So, on the object model level, class methods/attributes belong to
> the Package part of a class, while instance methods/attributes
> belong to the Class part of a class - insofar as they're made
> distinct by use of my/our.
Well, currently in the prototype, class attributes defined with "our"
are stored in the Classes symbol table (which is inherited from
Package). Discussions with autrijus lead me to not address class
attributes defined by "my", since he felt they would be better
addressed as "normal" variables within the scope of the class body.
This is somewhat of an implementation detail, however, I think it may
also play a part in how these things work. For instance, in the
following example, is "$.foo" a class attribute? or just a local
variable for the inner block?
class Bar {
our $.bar;
{
my $.foo;
}
}
I assume that the leading "$." is what makes the difference, however,
IIRC the "$." is just part of the name, and no more special than
that. Which means that I can choose that name (yes, it is evil, but I
assume I can still do it).
But given that the variable will be accessible to all methods via the
closure mechanism, the only thing missing I think is the ability to
get at the variable via introspection.
Now, as for class methods, I suppose it is possible to just stash
then in the classes symbol table like with variables. However, do we
then loose the method call syntax? This also means that they would
not (directly) be inheritable since inheritence moves along
superclass lines, and not with @ISA. I am also not sure what you mean
about multi-methods either, could you please explain more?
Thanks,
Stevan
<tic>
<glazed, far-off look in the eye>
INCOMING!!!!!!
<opens desk drawer, rummages around for hand grenade>
.
.
.
Hunh. Who'da thunk - apparently even *flame* wars can trigger
post-traumatic stress disorder...
Stevan Little wrote:
> Now, as for class methods, I suppose it is possible to just stash then
> in the classes symbol table like with variables. However, do we then
> loose the method call syntax?
I think not. But the current notion seems to drift closer to my
idea of "free methods" versus "slot calls". To express that in therms
of your initial equation
object == state + behavior
would be expressed on the first meta level as
class == data(structure) + code(structure)
which means that the compiler syntactically splits the class
definition into an active and a passive part. Call this symmetry
breaking if you like. The same asymmetry exist in the method call
syntax
$foo .action;
because the compiler compiles that into a lookup of 'action' from
the method part of the MIR (Meta Information Repository, World in Russian)
followed by a dispatch on the actual runtime type referred to by $foo.
In other words, the name that connects the compile and runtime is 'action'.
If this name shall be retrieved from the instance at runtime without going
through the method dispatcher I have proposed to wrap-up the name as
$foo :action;
Without the $ sigil a bare foo is interpreted as a name lookup in the code
part of the MIR which is the union of all subs and methods---subs beeing
nullary methods so to speak. That gives
foo .action; # dispatch action on retval of foo
and
foo :action; # bind named param in the call to foo.
These two things are on the tightest precedence level, which in my eyes
makes . and : meta sigils or some such.
We could actually combine these with the idea of the current name beeing
a sigilled underscore and '_._' denoting the current method on the current
topic, and '_:_' the current key from the current topic :)
Hmm, these would make {_} the closure of the current continuation or so.
> This also means that they would not
> (directly) be inheritable since inheritence moves along superclass
> lines, and not with @ISA. I am also not sure what you mean about
> multi-methods either, could you please explain more?
Symmetric MMD at least has the meaning that the above mentioned asymmetry
doesn't exist for infix ops on the syntactic level:
$x foo $y;
which neither means
($x foo) $y; # calculated prefix op from postfix foo
nor
$x (foo $y); # calculated postfix op from prefix foo.
I can't speak for metric MMD, though. But IIRC, the metric is
'sum of superclass hops'.
--
Okay, I think I understand now. So if all class methods were multis,
then we would not need inheritance. The MMD would use the (super|sub)
class relationships, and be able to call (super|sub)classes
automagically.
However, IIRC, the "everything is a multi-method" proposal was not
accepted. Could this then be just a restricted case of multi-methods?
So all class methods would just use MMD dispatch rules on the
invocant parameter, therefore allowing an implicit pseudo-inheritence
to take place?
Larry, is this what you were thinking?
Stevan
ste...@iinteractive.com wrote:
> class Bar {
> our $.bar;
> {
> my $.foo;
> }
> }
>
> I assume that the leading "$." is what makes the difference, however,
> IIRC the "$." is just part of the name, and no more special than
> that. Which means that I can choose that name (yes, it is evil, but I
> assume I can still do it).
Ummmm... I'm not sure that allowing $. injection from the nested blocks
is a good thing. I don't think it's ambiguous, but to me it looks weird
and confusing - if a user put the variable in the nested block like
that, it's almost certain he actually meant to write
{
my $foo;
}
and the . was put there erroneously.
>
> But given that the variable will be accessible to all methods via the
> closure mechanism, the only thing missing I think is the ability to
> get at the variable via introspection.
>
> Now, as for class methods, I suppose it is possible to just stash
> then in the classes symbol table like with variables. However, do we
> then loose the method call syntax?
Well, if it belongs to the module part of the class, the syntax would be
Bar::method(...), right?
> This also means that they would not (directly) be inheritable since
> inheritence moves along superclass lines, and not with @ISA.
Well, namespaces should generally be inheritable. How else would lexical
namespaces inject variables from outer block into the inner? I guess at
the package level declaration that Class1 is Class2 would just inject
symbols from one module into the other? (yes, this is kinda handwavy :/ )
> I am also not sure what you mean about multi-methods either, could
> you please explain more?
Uhm. I'm not sure either. :) The way I read Larry's mail, multimethods
use .isa operator to detect whether $foo belongs to Foo. And for every
class, Foo.isa(Foo) is true (this is exceptional behaviour of .isa). So
multi bla (Foo $f);
would accept both bla(Foo.new()) and bla(::Foo). (Larry, please correct
me on this if I'm misparaphrasing you :) )
Anyway, this makes me cringe on a certain level, although I'm not sure I
can put the reason into words. Strictly speaking, it's bad only if there
are cases where multimethod should behave differently for a class and an
instance. One case I can think of is
class AutoCreation { ... };
multi dwimmy_instance(class AutoCreation $obj) { $obj.new() }
multi dwimmy_instance(AutoCreation $obj) { $obj }
(I pulled the syntax out of my nose for this). Anyhow, this looks like a
pretty contrieved usage. I'm still trying to think of a non-contrieved,
more realistic situation when you might actually want something like this.
Miro
You are probably right, but are the twigils actually special? or is
it just a naming convention.
>> But given that the variable will be accessible to all methods via
>> the closure mechanism, the only thing missing I think is the
>> ability to get at the variable via introspection.
>>
>> Now, as for class methods, I suppose it is possible to just stash
>> then in the classes symbol table like with variables. However, do
>> we then loose the method call syntax?
>>
>
> Well, if it belongs to the module part of the class, the syntax
> would be Bar::method(...), right?
Yes, but the . is the method invocation operator, the :: is package
symbol table access. I think they really mean different things.
>> This also means that they would not (directly) be inheritable
>> since inheritence moves along superclass lines, and not with @ISA.
>
> Well, namespaces should generally be inheritable.
I don't think this is true, this is a Perl 5-ism.
> How else would lexical namespaces inject variables from outer block
> into the inner?
I think you are confusing namespacing with scoping. The scope rules
are as such that the inner can access see the outer.
> I guess at the package level declaration that Class1 is Class2
> would just inject symbols from one module into the other? (yes,
> this is kinda handwavy :/ )
I don't think this would be so, unless you explicitly asked for it.
>> I am also not sure what you mean about multi-methods either,
>> could you please explain more?
>
> Uhm. I'm not sure either. :) The way I read Larry's mail,
> multimethods use .isa operator to detect whether $foo belongs to
> Foo. And for every class, Foo.isa(Foo) is true (this is exceptional
> behaviour of .isa). So
(sidebar: it will probably use .does actually, but this an as yet
unresolved detail)
> multi bla (Foo $f);
>
> would accept both bla(Foo.new()) and bla(::Foo). (Larry, please
> correct me on this if I'm misparaphrasing you :) )
I am not 100% sure this is true given basic multi-method behavior,
however there is no reason we cannot either special case this, or do
some other kind of trickery to make it happen.
> Anyway, this makes me cringe on a certain level, although I'm not
> sure I can put the reason into words. Strictly speaking, it's bad
> only if there are cases where multimethod should behave differently
> for a class and an instance. One case I can think of is
>
> class AutoCreation { ... };
>
> multi dwimmy_instance(class AutoCreation $obj) { $obj.new() }
> multi dwimmy_instance(AutoCreation $obj) { $obj }
I think we need some ability to differentiate between the class
version of Foo and the instance version of Foo.
Stevan
dot sigils are not actually special. They are required on has-variables
and forbidden on all other. Changing them to be optional is trivial, or
so I hope.
(I believe that sigils or twigils should not indicate scope, duration or
type. The distinctin between the major variable types is useful, further
distinction is not.)
> I don't think this is true, this is a Perl 5-ism.
Perl 5 isn't a synonym for "bad". "Perl 5-ism" can be construed as
positive or negative. Please do elaborate.
Juerd
--
http://convolution.nl/maak_juerd_blij.html
http://convolution.nl/make_juerd_happy.html
http://convolution.nl/gajigu_juerd_n.html
> On Oct 18, 2005, at 6:56 AM, Miroslav Silovic wrote:
> > Uhm. I'm not sure either. :) The way I read Larry's mail,
> > multimethods use .isa operator to detect whether $foo belongs to
> > Foo. And for every class, Foo.isa(Foo) is true (this is exceptional
> > behaviour of .isa). So
> (sidebar: it will probably use .does actually, but this an as yet
> unresolved detail)
Consider it fairly resolved.
(What? Using type as a marker of potential coercion? Yep!)
-- c
>> Ummmm... I'm not sure that allowing $. injection from the nested
>> blocks is a good thing. I don't think it's ambiguous, but to me it
>> looks weird and confusing - if a user put the variable in the nested
>> block like that, it's almost certain he actually meant to write
>>
>> {
>> my $foo;
>> }
>>
>> and the . was put there erroneously.
>
Gack, furthermore, I was thinking 'has' and reading/writing 'my'. Sorry. :(
>
>>> But given that the variable will be accessible to all methods via
>>> the closure mechanism, the only thing missing I think is the
>>> ability to get at the variable via introspection.
>>>
>>> Now, as for class methods, I suppose it is possible to just stash
>>> then in the classes symbol table like with variables. However, do
>>> we then loose the method call syntax?
>>>
>>
>> Well, if it belongs to the module part of the class, the syntax
>> would be Bar::method(...), right?
>
>
> Yes, but the . is the method invocation operator, the :: is package
> symbol table access. I think they really mean different things.
Yes.
Now to be literal-minded, if Foo is a class...
Foo::method() would mean package(Foo)::method() (do package access, call
method. No invocant, but you can access the proper child of Foo as $*MODULE)
Foo.method() would mean Class::method(Foo:) (forward to metaclass, with
class as an invocant)
Nicely symetric, and (IMHO) completely unintuitive. So what exactly
means to say that a class is a module?
>
>>> This also means that they would not (directly) be inheritable
>>> since inheritence moves along superclass lines, and not with @ISA.
>>
>>
>> Well, namespaces should generally be inheritable.
>
>
> I don't think this is true, this is a Perl 5-ism.
>
>> How else would lexical namespaces inject variables from outer block
>> into the inner?
>
>
> I think you are confusing namespacing with scoping. The scope rules
> are as such that the inner can access see the outer.
Fair enough. I was talking about implementation - this means you need
symbol table import and shadowing (with the symbols defined in the inner
scope). Same (or similar) policy could be used for class methods and
attributes.
>
>> I guess at the package level declaration that Class1 is Class2 would
>> just inject symbols from one module into the other? (yes, this is
>> kinda handwavy :/ )
>
>
> I don't think this would be so, unless you explicitly asked for it.
I'm not sure I understand, why not?
>> Anyway, this makes me cringe on a certain level, although I'm not
>> sure I can put the reason into words. Strictly speaking, it's bad
>> only if there are cases where multimethod should behave differently
>> for a class and an instance. One case I can think of is
>>
>> class AutoCreation { ... };
>>
>> multi dwimmy_instance(class AutoCreation $obj) { $obj.new() }
>> multi dwimmy_instance(AutoCreation $obj) { $obj }
>
>
> I think we need some ability to differentiate between the class
> version of Foo and the instance version of Foo.
I think we agree here. :)
Miro
Dot sigils drive accessor generation, which essentially hoists an
ordinary variable into the object's namespace. They are not just
commentary.
: (I believe that sigils or twigils should not indicate scope, duration or
: type. The distinctin between the major variable types is useful, further
: distinction is not.)
Eh? Sigils are for type, twigils are precisely for doing weird things
with scope or duration.
* global scope
+ currently compiling scope
? currently compiled scope
= current file/pod scope
< current $/ scope
^ current signature scope
: make $:foo equivalent to :foo($foo) (conjectural)
Larry
"has" can do this without the dot, in theory.
> : (I believe that sigils or twigils should not indicate scope, duration or
> : type. The distinctin between the major variable types is useful, further
> : distinction is not.)
> Eh? Sigils are for type, twigils are precisely for doing weird things
> with scope or duration.
This doesn't change how I feel about them, though :)
Still, I do like twigils for automatically existing variables, like
%*ENV and &?SUB. Here, I do see the need and use for indicating scope
and duration, but I mostly read the twigil as: this variable is
/special/.
Global scope needs a twigil because in most cases, the declaration would
be nowhere to be found. Declaring superglobals doesn't necessarily make
sense anyway.
The ^ twigil is needed for the same reason: declaration is implicit.
Object attributes are declared by the Perl coder and initialized by
code. I know where they come from, and if not, I can look it up very
easily. There is no twigil for "my" and "our" variables, and we don't
need any. Same for "has": declaration is explicit.
> : make $:foo equivalent to :foo($foo) (conjectural)
This one is new to me. I'm not sure I understand what it's used for. Is
there already some documentation about it?
And does this mean $:foo is no longer a private $.foo? (which could be a
very good thing, by the way) What replaces that?
It's in my copy of S06, which I haven't checked in yet. By the way,
the form is intended to work in either signatures or as an rvalue,
and in signatures replaces + to mark named args. + becomes the marker
for required attributes (assumed on initial positional args).
: And does this mean $:foo is no longer a private $.foo? (which could be a
: very good thing, by the way) What replaces that?
The absence of a dot creates a private attribute. We decided it should
be even easier to declare a private attribute than a public one, so it's
just
has $foo;
and then it is visible only in the lexical scope.
Larry
This takes away my objections to the dot twigil entirely.
Is there an AES commit feed available somewhere?
--
Gaal Yahas <ga...@forum2.org>
http://gaal.livejournal.com/
> I think Perl 6's OO system has the potential to be to OO programming what
> Perl 5, etc was to text processing. This, I believe, is in large part due to
Sorry for replying so late. Thought it seems appropriate to post this in
this time of "Perl 6 fears" and rants threads...
Well, the point is that it is interesting to note that "text processing"
is an _application area_, whereas "OO programming" is a programming
language paradigm.
Despite the intro above, this is not meant to be a rant or to express a
fear. But it is intended to raise a meditation.
After all, being known for text processing capabilities may be somewhat
restictive and not faithful of Perl's (including Perl 5) full
potentiality, but "OO programming" is somewhat immaterial either, the
"only" relevance being the suitability for big projects management.
Michele
--
Ira Kane: If I was a giant nasty alien bird in a department store, where
would I be?
Harry Block: Lingerie.
Ira Kane: Not you, the bird.
Harry Block: Lingerie.
- "Evolution", Ivan Reitman
Allow me to clarify.
Perl 5 (and below) are known by outsiders (non-perl users) as being a
really good language for processing text. Ask any friend you know who
has had little or no exposure to Perl and they will probably tell you
this. Of course they will also tell you that it is all line noise,
etc, etc etc. This is the most common perception of Perl by those
people who have (for the most part) never encountered it.
I think that Perl 6 may become like that for OO. When people who have
never used or encountered Perl 6 talk about it, they will say, "I've
never used it, but I hear it has lots of really cool OO features".
Just as now they the same thing re: text-processing.
Sure, this means nothing to people who are actually using it, but
this is mostly about outside perception. These kinds of things are
sometimes what will bring people to the language initially, so they
are not to be taken lightly.
> Despite the intro above, this is not meant to be a rant or to
> express a fear. But it is intended to raise a meditation.
>
> After all, being known for text processing capabilities may be
> somewhat restictive and not faithful of Perl's (including Perl 5)
> full potentiality,
Of course not, Perl is also used for CGI, but you can do that better
with Java now (which is a real language cause it's compiled)
;)
People who are not familiar with a language tend to rely heavily on
the common "knowledge" about that language. And also tend to hold
tightly to the myths and pseudo-facts surrounding their own
languages. The combination of these two things tends to lead to silly
statements like the one above.
> but "OO programming" is somewhat immaterial either, the "only"
> relevance being the suitability for big projects management.
The idea that OO is only relevant for big projects is just as silly
as saying that Perl is only good for text processing.
Sure I would not use OO to write a small utility script, but the
modules I access in the script would probably be written using OO.
And those may be very small modules too, but their re-usability is
greatly enhanced by various OO features (encapsulation, ability to
compose-into or use within another class, etc etc etc). This kind of
thing (IMHO) is why so many people are being drawn to Python and
Ruby. Hopefully Perl 6 can draw them back.
Stevan
>> Well, the point is that it is interesting to note that "text processing"
>> is an _application area_, whereas "OO programming" is a programming
>> language paradigm.
>
> Allow me to clarify.
>
> Perl 5 (and below) are known by outsiders (non-perl users) as being a really
> good language for processing text. Ask any friend you know who has had little
> or no exposure to Perl and they will probably tell you this. Of course they
> will also tell you that it is all line noise, etc, etc etc. This is the most
> common perception of Perl by those people who have (for the most part) never
> encountered it.
ack!
> I think that Perl 6 may become like that for OO. When people who have never
> used or encountered Perl 6 talk about it, they will say, "I've never used it,
> but I hear it has lots of really cool OO features". Just as now they the same
> thing re: text-processing.
I see your point. And I admit I slightly misunderstood you. In any case
people who don't know Perl and think it's great for text processing may be
eventually become interested in it if they will have to do... ehm... text
processing.
But even if Perl will gain a name for it's "cool OO features", will it
become an actual interest for someone if he/she has to do... OO stuff?!?
The basic idea being that one is attracted by a language or another if its
features are well suited for some specific application he/she has in mind.
Not for its features as an abstract thing.
Also, Perl6 OO capabilities are already above the top of my head. But
maybe that's just me. Whatever, I guess that the {casual,average}
programmer may be scared by its richness and complexity.
>> After all, being known for text processing capabilities may be somewhat
>> restictive and not faithful of Perl's (including Perl 5) full
>> potentiality,
>
> Of course not, Perl is also used for CGI, but you can do that better with
> Java now (which is a real language cause it's compiled)
Of Perl is IMHO even _too_ known for CGI.
>> but "OO programming" is somewhat immaterial either, the "only" relevance
>> being the suitability for big projects management.
>
> The idea that OO is only relevant for big projects is just as silly as saying
> that Perl is only good for text processing.
ack. I was oversimplifying.
> Sure I would not use OO to write a small utility script, but the modules I
> access in the script would probably be written using OO. And those may be
> very small modules too, but their re-usability is greatly enhanced by various
> OO features (encapsulation, ability to compose-into or use within another
> class, etc etc etc). This kind of thing (IMHO) is why so many people are
> being drawn to Python and Ruby. Hopefully Perl 6 can draw them back.
Also, some methods will be very handy even for non-patently-OO blocks,
e.g. the .pos() one which will replace the somewhat ad hoc pos() function
or the .subst() one (or whatever it is called, I can't remember exactly)
which hopefully will get rid of the "why doesn't s/// work inside a map()"
question...
Michele
--
Have you ever stopped to consider that what is crashing
your Perl is sitting between the keyboard and chair?
- Sherm Pendley in clpmisc, "Re: Perl IDE" (edited)
Sure, they're above my head too. Which should be obvious by now... :-)
: But maybe that's just me. Whatever, I guess that the {casual,average}
: programmer may be scared by its richness and complexity.
But we're trying to design the OO features (indeed, all of Perl 6)
such that you can usefully cargo cult those aspects that are of
immediate interest without being forced to learn the whole thing.
It's not the number one design goal, but it's right up there.
Larry
> But we're trying to design the OO features (indeed, all of Perl 6)
> such that you can usefully cargo cult those aspects that are of
> immediate interest without being forced to learn the whole thing.
> It's not the number one design goal, but it's right up there.
So you're talking about a "positive", tame form of cargo cult, giving the
latter a good name...
Michele
--
you need a brain hack. or a brain of any sort. try a nematode first. the
small incremental improvement won't be such a shock. then you can
graduate to segmented worm brains.
- Uri Guttman in clpmisc, "Array Sort Using Regex Matching Fails"
Yes, I hack on more languages than just Perl. :-)
I use the term "cargo cult" for lack of a better term, and when I do
so I tend to use it as a verb rather than a noun. By "cargo culting"
I mean the process by which most of us learn to use new technology.
We appropriate inappropriately; we have to start using a thing wrongly
before we can start using it rightly. When we're very young, we learn
by picking up objects and stuffing them into our mouths. That's not
the appropriate way for an adult to treat most objects, but it's the
right way for a toddler to treat an object, and it's the parents'
responsibility to "child-proof" the room so the child doesn't chew
on power cords and such. So another way to say what I said is that
we're trying to make Perl 6 relatively safe for people who are just
starting to use bits of it inappropriately in order to learn it.
The term "cargo cult" also has some religious connotations, and to a
small extent I mean those too. The "religious" wars fought in computer
communities often arise among those with the least understanding of the
deep issues. People much more easily fall into tribal behavior than
into philosophical behavior. A deep understanding of bikesheds is that
they're meant to hold bicycles, and it doesn't really matter what color
they are. But when you first discover a bikeshed, you might mistake
it for a tribal monument of some sort. It's obviously more important
than the bicycle; after all, it's bigger. And we might attract more
new husbands or wives from other tribes if we paint it attractively.
So we spend a lot of time painting the outside while the bicycle is
inside rusting. And the bicycle is what's important, because we should
be reaching out to other tribes, not waiting for them to come to us.
Well, hey, my metaphor is probably painted the wrong color. But maybe
it's only by using analogies inappropriately that we can begin to
understand how to use them appropriately.
Larry
Interesting. I just finished reading Stroustrup's thoughts on evolution
and language design at:
http://www.research.att.com/~bs/rules.pdf
and noticed he is similarly concerned about making his language easier
for the casual/novice programmer:
"If you are a physicist needing to do a few calculations a week,
an expert in some business processes involving software, or a
student learning to program, you want to learn only as many
language facilities as you need to get your job done."
He also argues in favour of general features over specialized ones:
"C++0x will not be a "Windows language" or a "Web Language" ... "
which reminded me of people characterizing Perl as a "text processing
language" or a "CGI language".
Just as Perl 5 evolved Perl 4 from a "scripting/text processing language"
to a general purpose one (with CPAN modules filling many specialized
niches), Perl 6 seems to be evolving still further down that path,
being applicable to more and varied domains than Perl 5 was.
/-\
Send instant messages to your online friends http://au.messenger.yahoo.com