Closed Ovid closed 2 years ago
I would like to advocate renaming has
in favour of slot
.
slot
explicitly describes what is being declared (like class
)slot
is a noun so it can be pluralized if syntax for declaring multiple slots is simpler to implement: slot $cache :private :handles(get) = Hash::Ordered->new;
slot $created :private :reader :immediate = time;
slot $max_size :optional :reader :isa(PositiveInt) = 20;
slots {
$cache :private :handles(get) = Hash::Ordered->new;
$created :private :reader :immediate = time;
$max_size :optional :reader :isa(PositiveInt) = 20;
}
If really wacky syntax was acceptable the attributes can be applied at the slots
declaration to save even more typing:
slots :private :required {
$cache :handles(get) = Hash::Ordered->new;
$created :reader :immediate = time;
}
slots :private {
$max_size :optional :reader :isa(PositiveInt) = 20;
}
# transforms/translates to
#
# slot $cache :private :required :handles(get) = Hash::Ordered->new;
# slot $created :private :required :reader :immediate = time;
# slot $max_size :optional :reader :isa(PositiveInt) = 20;
This bulk declaration option does add another dimension of difficulty to detecting illegal attribute combinations on a slot
I suggest the definition of versions be:
v? ( [0-9]{1,3} \. )? ( [0-9]{1,3} \. )? [0-9]{1,3}
(without spaces)
In other words:
v
Spaces and non-digit numbers (other than optional leading 'v' or periods between the numerical fields) are not allowed. This includes underscores.
This allows us to have:
eval
ing versions variables)////Allowing 1/2/3 part versions seems reasonable, but I would keep the leading v
mandatory. If you allow 2 part versions without the v
, it will lead to confusion with perl's classic version scheme.
Please consider also anonymous classes, eg:
my $foo1 = class isa Foo does Bar { }->new;
my $foo2 = class isa Foo does Bar2 { sub baz { } }->new;
ad builders:
builder method IMHO has no use as a standalone method as well as its prefix build
may be confusing if the class will provide some Builder pattern(s). The only reasonable use case is overloading and calling the parent's builder.
What about definition syntax:
build property => sub { ... };
@happy-barney Would very much love to have anonymous classes, but I suspect that will be v2. There are also open questions about what part of the behavior is compile-time versus runtime and that will impact this, also.
Traditional Perl OO (bless any reference) allows to do things in pure Perl which would need support by the core for Cor classes. Here are some questions:
_DUMP
as by Class::Std?ref $cor_object
will return something meaningful, but everything beyond that seems opaque.$object = bless $here_are_your_guts, $class
. Is there a Cor equivalent?In addition for @HaraldJoerg 's comment I'd like to mention also re-bless.I found it quite useful when refactoring certain types of applications.
Would it be possible to add this possibility? Limitation "rebless only into child" will be acceptable, special syntax to call missing constructors as well.
eg:
my $foo = Foo->new;
$foo->Cor::UPGRADE (Bar => (bar_property => 1));
@clscott said:
I would like to advocate renaming
has
in favour ofslot
.
- It doesn't work anything like Moose, Moo, etc. so shouldn't have the same name
slot
explicitly describes what is being declared (likeclass
)- Searching/Googling for "perl slot" shouldn't turn up a lot of unrelated documentation
- Searching for "perl has" in does not return very helpful results in Google and DDG
slot
is a noun so it can be pluralized if syntax for declaring multiple slots is simpler to implement:
On one hand I don't agree with point 1 because it shouldn't matter what Moose/Moo do when making Corinna the best it can be. Also Raku does use the word "has".
I otherwise support this proposal in that my sensibilities prefer declarations like this to be nouns and points 2 thru 5 are good.
Perl should support authority declarations like Raku does, in addition to the version. If not in its first version then later.
I will argue against the optional "abstract" declarator because this doesn't do anything that a role doesn't do. If you want an "abstract" class then you actually want a role. If you think an "abstract" class is different than a role then please explain why we should have both.
Everything official should use the full name Corrina, such as Universal::Corrina
rather than Universal::Cor
.
The term Cor should generally go away as confusing, and not be etched in stone by naming a package etc after it.
What benefit is multiple inheritance when we have roles?
Addressing various issues.
has
to slot
.I've been trying to strike a balance between the familiar and the new. It's hard. I'll think about it.
For inheritance, sequencing matters and is (relatively) easy to control. This is not the case for roles. Thus, when writing something like Test::Class::Moose
where you need to control exactly when the parent methods fire, building it on roles, where the ordering isn't guaranteed, is a problem. That problem goes away with inheritance.
That being said, most of the time I would favor a role for this.
I keep thinking about this and then forgetting it. I should put something in UNIVERSAL::Corinna
. I had considered the MOP, but I don't think we will have that in V1 and debugging support is critical.
build property => sub { ... };
.Currently, Moo/se allow one to alter building in a subclass. Corinna was designed to support his. However, that can be a pretty huge encapsulation violation, not to mention a source of a bugs.
class Parent {
has $foo :builder;
method _build_foo () { ... }
}
class Child isa Parent {
has $foo :builder;
method _build_foo () { ... }
}
So now have the child changing a parent's private data. Oops. So yeah, that was a mistake. Definitely need to think about this.
The parent could always do this:
class Parent {
has $foo :builder;
private method _build_foo () { ... }
}
class Child isa Parent {
has $foo :builder;
method _build_foo () { ... }
}
But that raises interesting questions about the semantics of private
methods (which I still need to address) and why is a builder a method at all? It's designed to be a bit of behavior that fires zero or one time per instance. It doesn't behavior like an attribute or a method at all.
As for "multiple inheritance," I thought about just saying "no" because I agree that with roles, there are far better solutions. Single inheritance minimizes (doesn't eliminate) issues that C3 solves and even then, inheritance should be heavily discouraged.
I think MI is a mistake. In OO, a subclass is generally viewed as a more specific version of a parent class, just as Car
and Truck
are both more specific versions of a Vehicle
. The researchers behind the fascinating (and dead) Beta programming language researched how to incorporate an analog to MI in their language, but they wanted it "clean." So they looked for examples of MI where the children really were more specific versions of their parents. They couldn't find any. Instead, they found MI being used exclusively to borrow bits and pieces of behavior. Perl solves that with roles.
I'm happy to say "no MI" unless anyone can make a strong case beyond "I personally like it."
In Raku I believe BUILD is a submethod rather than a method.
The only multiple inheritance is-a examples I can think of off hand are contrived examples, such as the diamond relationship of parallelogram, rhombus, rectangle, and square. And there's no good reason to complicate a programming language to support features that logically only make sense for such contrivances. Multiple inheritance made more sense in languages that didn't support composition like with roles, but when you have that there's no reason for it at all.
@Ovid A general piece of feedback I can give for Corrina is if in doubt leave it out. If anything is questionable, default to omitting it out of version 1. So this includes things that have been used before but are arguably bad practices or code smells. Or if they're unnecessarily complicated to implement for their value.
To recap, here are a few things discussed that I recommend omitting entirely:
Right now the only special scalar alphabetic Perl variables are $a
and $b
. This proposal would add two more, $self
and $class
, which we should avoid as much as possible. Why not follow the precedent of __SUB__
and declare two new tokens: __SELF__
and __CLASS__
?
In the proposal, it's stated that $self
is implicit and already injected into the scope of instance methods. How will this affect @_
? I suggest that the array's first element no longer be $self
and instead the array will be like for regular subroutines.
I agree with both of @seav comments.
@seav __SELF__
and __CLASS__
tokens are an interesting idea, but really, the $self
and $class
are only "special" in the scope of Corinna. I've struggled a few times to see how to get this right. I was looking at self->method
at one point, but that means you can't name a class self
and Perl would need to recognize new keywords. However, by keeping the $
sigil, Perl developers have a familiar environment and they can understand instantly what those mean. As for __SELF__
, you'd be introducing a token that, unless I'm mistaken, is semantically unrelated to all other tokens: __LINE__
, __PACKAGE__
, __END__
, __DATA__
(or others I'm forgetting?). I'm unsure if that's the best way to go.
As for @_
not having the invocant, that's certainly worth considering. For the most part, I've been trying to work hard to eliminate a direct need for @_
in most cases.
In case you will not provide multiple inheritance please do not differentiate roles and classes anywhere, ie, make for example isa
and does
to be synonyms. Also please provide expressions like requires => 'extends class Foo and does role Bar
I suggest having $args to go along with $self and $class, and $args would contain the arguments minus the invocant $self. The $ syntax is also flexible enough to hold either an arrayref or hashref or some other type representing either positional or named arguments. In contrast with @_, which in Raku exists along with a %_ counterpart.
@happy-barney I disagree on making isa/does synonyms and not differentiating roles/classes. Conceptually classes and roles mean completely different things. If an object represents a thing, then the object's class, and also its parent classes, all say what the object IS, while roles represent capabilities, they are what the object CAN DO. There's a huge difference between identity, what something is, and interface, what something can do. Eliminating multiple inheritance doesn't change this. On the other side, I like your "requires" proposal in principle.
Further to my prior comment, best practice is to name classes after nouns, or at least the classes that represent a thing and aren't just utilities. Whereas, roles should be named after adjectives or verbs.
@happy-barney Actually on further thought I actually agree with a semblance of your first request, but for a different reason.
I still don't agree with making "isa/does" synonyms. But what I do agree with is the concept of having a single method/operator one can use which interchangeably accepts either a class name or a role name and true if the test object either is that class or does that role.
The thing I see as important is that this unified operator/method would behave the same way as a generic type/constraint declaration later when such are added. For example, if someone declared "Int $foo" then Int could be either a class or a role, and it doesn't matter in practice which. So the unified method/operator is asking, can the given value/object be used where something of the given type is expected.
But then what we ACTUALLY need is something much more generic, not just accepting a package name but alternately a predicate subroutine that returns true if the test value is of the right data type, and the value in question may not be an object or blessed at all.
I see no point in having a unified isa/does unless it also does the other things I said, and since we're not having type constraints in Corinna version 1, I expect this unified thing will also be left out of version 1, to be considered later.
@duncand your second comment is actually what I as talking about. I didn't say "make them synonyms", I used that as an example which will provide desired effect.
On other hand, I don't care about class/role/whatever. I do care about current context. Context doesn't distinguish whether it is class or instance, it just provides you information you need to do your work. It doesn't matter whether it was provided via method argument, via instance constructor, or it is class static property or environment variable (you can see dependency injection here, but do not focus on it)
Edit: ad unified isa/does, I think this should rather be in the scope of creating class literals
in perl and providing meaningful set of operations on it.
There's a basic thing I don't think was addressed yet.
I am assuming that the Corinna built-in to Perl would use a brand new opaque data type for its objects, something that is NOT a hashref/arrayref/etc.
So what would ref $obj
and similar operators return?
I suggest it return OBJECT
rather than HASH
etc.
Closing old tickets because the MVP has been accepted. We'll start fresh with real code.
Use this ticket to leave feedback on the Cor class proposal.