Perl-Apollo / Corinna

Corinna - Bring Modern OO to the Core of Perl
Artistic License 2.0
157 stars 19 forks source link

Class feedback #5

Closed Ovid closed 2 years ago

Ovid commented 4 years ago

Use this ticket to leave feedback on the Cor class proposal.

clscott commented 4 years ago

I would like to advocate renaming has in favour of slot.

  1. It doesn't work anything like Moose, Moo, etc. so shouldn't have the same name
  2. slot explicitly describes what is being declared (like class)
  3. Searching/Googling for "perl slot" shouldn't turn up a lot of unrelated documentation
  4. Searching for "perl has" in does not return very helpful results in Google and DDG
  5. 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

xsawyerx commented 4 years ago

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:

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:

haarg commented 4 years ago

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.

happy-barney commented 4 years ago

Please consider also anonymous classes, eg:

my $foo1 = class isa Foo does Bar { }->new;
my $foo2 = class isa Foo does Bar2 { sub baz { } }->new;
happy-barney commented 4 years ago

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 { ... };
Ovid commented 4 years ago

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

HaraldJoerg commented 4 years ago

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:

happy-barney commented 4 years ago

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));
duncand commented 3 years ago

@clscott said:

I would like to advocate renaming has in favour of slot.

  1. It doesn't work anything like Moose, Moo, etc. so shouldn't have the same name
  2. slot explicitly describes what is being declared (like class)
  3. Searching/Googling for "perl slot" shouldn't turn up a lot of unrelated documentation
  4. Searching for "perl has" in does not return very helpful results in Google and DDG
  5. 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.

duncand commented 3 years ago

Perl should support authority declarations like Raku does, in addition to the version. If not in its first version then later.

duncand commented 3 years ago

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.

duncand commented 3 years ago

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.

duncand commented 3 years ago

What benefit is multiple inheritance when we have roles?

Ovid commented 3 years ago

Addressing various issues.

  1. I'm not opposed to renaming 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.

  1. Sometimes you want an abstract class to be a base class and not a role.

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.

  1. Authority declarations might be v2 or later. But yet, I like that.
  2. I will work to start changing "Cor" to "Corinna" (note the spelling) everywhere.
  3. Debugging support for class internals.

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.

  1. Proposal to change the builder to 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.

Ovid commented 3 years ago

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

duncand commented 3 years ago

In Raku I believe BUILD is a submethod rather than a method.

duncand commented 3 years ago

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.

duncand commented 3 years ago

@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:

seav commented 3 years ago

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__?

seav commented 3 years ago

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.

duncand commented 3 years ago

I agree with both of @seav comments.

Ovid commented 3 years ago

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

happy-barney commented 3 years ago

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

duncand commented 3 years ago

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.

duncand commented 3 years ago

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

duncand commented 3 years ago

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.

duncand commented 3 years ago

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

happy-barney commented 3 years ago

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

duncand commented 3 years ago

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.

Ovid commented 2 years ago

Closing old tickets because the MVP has been accepted. We'll start fresh with real code.