Perl-Apollo / Corinna

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

Class Phases #18

Closed Ovid closed 3 years ago

Ovid commented 4 years ago

Use this ticket to leave feedback for the Class Phases document.

perigrin commented 4 years ago

I left this as a comment on the original gist:

Let's start with I like moving the object construction / destruction protocol to phasers[1]. I think it neatly solves problems that we've had in Moo[se] where they're methods but not quite methods but actually methods because that's how we had to implement them but ... yeah. I also understand that the differences between Cor and say Universal::OBJECT mean that you can't just re-use the latter's protocol.

That said I think some of the names you have are poor choices. The existing phasers (BEGIN, INIT, CHECK) are all intransitive verbs (not requiring an object) or nouns, they carefully don't step on each each other's meanings (though BEGIN and INIT come pretty close), and they don't step on other parts of the program flow (BEGIN is obviously very different from sub main{} or use or other things people might write in their own script).

CONSTRUCT when it's used as a noun is the end result of the construction process. When CONSTRUCT is a verb it's a transitive verb and means to build something from components. CONSTRUCT is also related the entire process of object construction (literally being the verb form of the latter concept). All of these things make it seem like you're providing the entire constructor in this phase. A better term would be COMPOSE which is an intransitive verb meaning among other things "to settle, adjust, or reconcile", unfortunately it's related to the process of adding roles to the class definition. Another better term would be CONTRIVE which is an intransitive verb meaning "To invent or fabricate, especially by improvisation." and has no confusion to other parts of the object construction process that I'm aware of.[2]

CONSTRUCT should replace NEW. NEW is neither a noun or a verb (it's an adjective most often) and it's easily confused with the constructor method/keyword new. My issues with CONSTRUCT mostly go away because the object of the verb is literally the object being generated which is the end result of object construction. Also by initially keeping this phase internal you have time to find a better intransitive verb to fit the intent (like ASSEMBLE). CONSTRUCT also now nicely mirrors DESTRUCT. (In Stevan's UNIVERSAL::Object NEW is called CREATE.)

ADJUST is fine if you want to step away from Moo[se]'s BUILD connotations.

For now I'm going to continue using your names.

You're over specifying NEW. NEW takes the list of construction arguments returned from all CONSTRUCT blocks and initializes the instance slots. Everything beyond that is a description of the default implementation and you shouldn't care what people do in their own time if you allow them to override it. I'm fairly confident people like LeoNerd and mst can come up with powerful reasons why you'd want to subvert the instance creation process and NEW is the only place you can easily do local to a single class.[3]

But a silly example of why you might want to override the default NEW is to warn if they're shadowing an upstream CONSTRUCT block's choice: NEW { warn "overriding parent's $" if ++$seen{$} > 1 for $self->CONSTRUCT_ARGS }.

In conclusion: I like the transition to phases, I love that you're introduction a first class object construction protocol as a series of explicit phases. I don't like the names you've picked for the phases and I'm happy to use color theory to explain why your bike-shed shouldn't be that color.

[1]: BTW you use the term phaser once in the DESTRUCT section but you never define it. Either define it in the intro or don't use it. [2]: An added bonus the negative connotations of CONTRIVE serve to illuminate that you're needing to think about your object's construction inputs some more [3]: If you were to for example remap construction args to SLOT names in a CONSTRUCT/CONTRIVE block, then another class downstream would be forced to override you to get back the original mappings. If you want that to happen only when an instance of this class is being created, with the protocol you have the only way to do that is in NEW/CONSTRUCT.

perigrin commented 4 years ago

Unrelated to my comment above, are we also going to have ROLE phases? In #cor LeoNerd suggested an APPLY block for role composition and I think that is potentially a fantastic idea to approach.

Ovid commented 4 years ago

I'll address some of the stuff above later. For now, I like the idea of an APPLY phase because it makes sense, but it might be more complicated than that. We might also need a COMPOSE phase. First, the role is composed (check for conflicts, generate requirements and figure out what the role provides) and then the role is applied. Having one phase without the other seems incomplete.

Ovid commented 3 years ago

This is resolved for the MVP. Further issues should be new tickets.