Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
Hi Larry and Nicolas, On Wed, Mar 25, 2020 at 9:24 AM Larry Garfield wrote: > > I'd like to propose something on the topic. > > I'm adding object literals to the mix because that's another feature of the > > language that we're missing a lot IMHO. > > Actually, there is one existing syntax for objects: (object) [...] > > [...] > > 2) What's its order of execution with the constructor? Vis, does the > constructor run after __create or before? Does __construct get any > parameters passed to it? That's the interesting question, though there's probably not any sensible answer that works for all use cases. However, if this kind of object casting (object literal notation) was limited to a certain (extendable) internal class (SPL Struct?), we could have just a final public function __construct(array $properties = []) that then delegates the validation to an abstract __validation() method. Would that at least be a useful alternative to constructor promotion? > In that syntax, you have to remember which one comes first. There's no > indication for the casual reader why > > public private $property; > > and > > private public $property; I'm wondering - are there any use cases where you would have private read and public write? If not, maybe this issue can be simplified by adding (f.ex.) "semiprivate" and "semiprotected" attribute keywords, the idea being that they give readonly access to the anything else. Though I think it would be great to have a nice, gracefully extensible attribute syntax, maybe it's not necessary to fight over that for this particular issue? > Either way, what matters is who is in charge of dealing with the issues > this might create. In both ways, what matters is that the original author > won't be bothered for things that are not its responsibility. "I broke your > app because you messed up with that private property on a class I > authored?" no my problem. About final, I usually prefer using the "@final" > annotation: it expressed exactly what I need to express as the author of > the code: "if you extend, you're on your own - but I'm not dictating what > you can/can't do either". > > I think this reasoning applies to my view on immutability :) :-) I can relate to that in general. Though for immutability, I'm not so sure anymore... -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
Am 25.03.20 um 15:24 schrieb Larry Garfield: > On Wed, Mar 25, 2020, at 5:57 AM, Nicolas Grekas wrote: >> Máté suggested this syntax and it has my preference over the one you menton >> Larry: doubling the visibility keyword could be enough to express >> read+write access: >> >> public private $property; <= public read access, private write access > > In that syntax, you have to remember which one comes first. There's no > indication for the casual reader why > > public private $property; > > and > > private public $property; > > are different. Plus, just looking at it, "wait, it's public and private? > WTF? That doesn't even make sense." > > It also doesn't extend gracefully to property accessors. Whatever accessors > do, if they ever get resolved, would conflict with that, and thus we'd have > that many more weird combinations of property metadata that are incompatible. > > The whole point of the syntax I proposed for asymmetric visibility is that > it's gracefully extensible, even if a little more verbose. (If we can find a > syntax that is less verbose while still gracefully extensible, I am on board > with that.) What about the following syntax: class X { public read private write $property; } That would play nicely with accessors: class Y { public read getProp private write setProp Type $property; // or this way around to make it clear which is the setter // and which is the property type public getProp read private setProp write Type $property; private function getProp() : ?Type {} private function setProp (Type $newValue) {} } It seems to me this would allow a clear syntax where you can selectively add accessor method for reading or writing or both, you can reuse existing setting methods when refactoring. Only the isset/unset accessors are missing. I am not sure if they are necessary: isset could be equivalent to ($obj->getProp() !== null) and unset to setting null, but I have not thought this through yet. Greets Dennis -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
On Wed, Mar 25, 2020, at 5:57 AM, Nicolas Grekas wrote: > > > https://hive.blog/php/@crell/improving-php-s-object-ergonomics > > Named parameters are a pretty tough topic. I think one of the main points > > of contention is that they make the parameters names part of the API > > contract, and as such also subject to LSP. Your proposal offers two > > possible ways to side-step this: First, by making named parameters opt-in > > with a special syntax {}. Second, by limiting them to constructors. The > > latter variant still exposes parameter names in the API, but at least does > > not require their preservation across inheritance, as constructors are > > excluded from LSP. I'm somewhat torn on this, because it makes named > > parameters unusable with the very large body of existing methods, and > > introduces an inconsistency in which methods can use named params and which > > don't. > > > > I'd like to propose something on the topic. > I'm adding object literals to the mix because that's another feature of the > language that we're missing a lot IMHO. > Actually, there is one existing syntax for objects: (object) [...] > > My proposal is to allow any class in the casting operator: (Foo) [...] > By default, all keys of the casted array would map to properties (with an > error when no matching property exists in the current visibility scope). We > would then allow for a new constructor method, either: > public function __create(array $values) > or: > public static function __create(array $values): static > > This method would take over the cast operator and decide how to construct > an instance of such a class. > > There is one drawback: accepted keys are not documented. Sure, property > declarations can give a big hint. > > But I think we can solve this issue later: it's not a blocker to still make > things work nicely. Also, this issue already exists with all methods that > accept an array of options - and we'll find a solution for those - either > using docblocks (there are already ways to use them for that) or using > attributes (would be the best of course, once we have them.) > > Note that this __create() method looks a lot like __unserialize(): it's > just called at a different place, but the implementations could be > essentially the same. Interesting. If that allowed populating private properties than that would be more capable than either of the initializer proposals that have been put forward. However, this alternate deserialization (as you note, it's basically __unserialize by another name) brings up other issues: 1) If it's a large number of properties, the __create method would still need to manually assign them to properties, just like constructors now. It wouldn't benefit from constructor promotion. 2) What's its order of execution with the constructor? Vis, does the constructor run after __create or before? Does __construct get any parameters passed to it? If it doesn't get called, that's yet another way to bypass the constructor and therefore bypass object validation. ("Make invalid states unrepresentable". If the properties don't logically make sense with each other that should be rejected as early as possible; syntactically if we can, via early validation if not.) Named parameters have neither of those issues, as data still only comes in via the constructor so both promotion and validation work fine. > Regarding the remainder, I think that all of readonly properties, > > asymmetric visibility and property accessors have their place and value, > > with some overlap between them. As you already mentioned, the previous > > property accessors proposal also included asymettric visibility as a > > special case, and that's how I would introduce it as well. > > > > Máté suggested this syntax and it has my preference over the one you menton > Larry: doubling the visibility keyword could be enough to express > read+write access: > > public private $property; <= public read access, private write access In that syntax, you have to remember which one comes first. There's no indication for the casual reader why public private $property; and private public $property; are different. Plus, just looking at it, "wait, it's public and private? WTF? That doesn't even make sense." It also doesn't extend gracefully to property accessors. Whatever accessors do, if they ever get resolved, would conflict with that, and thus we'd have that many more weird combinations of property metadata that are incompatible. The whole point of the syntax I proposed for asymmetric visibility is that it's gracefully extensible, even if a little more verbose. (If we can find a syntax that is less verbose while still gracefully extensible, I am on board with that.) --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
> > https://hive.blog/php/@crell/improving-php-s-object-ergonomics Thanks Larry, that's a good way to move forward on these topics. I generally like the ideal of combining property declaration and > constructors. I've had this on my mind for a while already, and also > received the same suggestion from a couple of other people (I think Nicolas > was one of them?) I confirm: that'd be super useful to clean the boilerplate. > large enough that I will often go with a quick and simple ad-hoc array > structure rather than declaring an explicit value object type. Yes > The main concern, as others have already mentioned, is that these inline > declarations can end up being quite verbose, especially once attributes get > involved. > I don't share this concern with attributes: they mix without any ambiguity, which is what matters. No syntax is perfect, ppl that prefer the current way will still be able to use it. > Named parameters are a pretty tough topic. I think one of the main points > of contention is that they make the parameters names part of the API > contract, and as such also subject to LSP. Your proposal offers two > possible ways to side-step this: First, by making named parameters opt-in > with a special syntax {}. Second, by limiting them to constructors. The > latter variant still exposes parameter names in the API, but at least does > not require their preservation across inheritance, as constructors are > excluded from LSP. I'm somewhat torn on this, because it makes named > parameters unusable with the very large body of existing methods, and > introduces an inconsistency in which methods can use named params and which > don't. > I'd like to propose something on the topic. I'm adding object literals to the mix because that's another feature of the language that we're missing a lot IMHO. Actually, there is one existing syntax for objects: (object) [...] My proposal is to allow any class in the casting operator: (Foo) [...] By default, all keys of the casted array would map to properties (with an error when no matching property exists in the current visibility scope). We would then allow for a new constructor method, either: public function __create(array $values) or: public static function __create(array $values): static This method would take over the cast operator and decide how to construct an instance of such a class. There is one drawback: accepted keys are not documented. Sure, property declarations can give a big hint. But I think we can solve this issue later: it's not a blocker to still make things work nicely. Also, this issue already exists with all methods that accept an array of options - and we'll find a solution for those - either using docblocks (there are already ways to use them for that) or using attributes (would be the best of course, once we have them.) Note that this __create() method looks a lot like __unserialize(): it's just called at a different place, but the implementations could be essentially the same. Regarding the remainder, I think that all of readonly properties, > asymmetric visibility and property accessors have their place and value, > with some overlap between them. As you already mentioned, the previous > property accessors proposal also included asymettric visibility as a > special case, and that's how I would introduce it as well. > Máté suggested this syntax and it has my preference over the one you menton Larry: doubling the visibility keyword could be enough to express read+write access: public private $property; <= public read access, private write access However, I generally think that the main value really is the readonly > properties as proposed in the recent RFC. Nowadays, a large fraction of the > classes I use are immutable value objects, for which public readonly > properties provide a much closer match to the semantics I want. > > I think that the problem with with-er methods is just that: It's a problem > with with-er methods. It's what happens when you try to shove immutability > into something that is not actually being used in an immutable manner. > Don't pretend things are immutable when they aren't... > I think "withers" solve the problem of immutability in a very pragmatic and convenient way. Take e.g. "private": it can be bypassed using closure rebinding or reflection, and this is really useful, e.g. to build "friendship" relations between classes in the same package (like in C++). On the other side, "final" is a real pain when needing to write proxies/decorators. It just blocks extensibility for no technical reasons really. Let me explain: If as a consumer, I DO want to extend a class, nothing will prevent me to do so. There is always the last resort solution, which is patching the too restrictive source class, either using a fork or some code rewriting tool. Either way, what matters is who is in charge of dealing with the issues this might create. In both ways, what matters is that the original author
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
> > For details, see the full writeup: > > https://hive.blog/php/@crell/improving-php-s-object-ergonomics An excellent writeup, thank you Larry. Peter
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
On 24-03-2020 10:21, Máté Kocsis wrote: > Hi Larry, > > In my opinion, one of the core assets of PHP is that it contains relatively > few syntactic sugar compared > to some other languages, e.g. C#. Maybe it's just me, but I believe it > makes the code written in PHP > easier to read. And I think this is what we should optimize for. Not for > saving a few lines of code, > but for making the code easier to read and understand. It's not just you. :) I fully agree with you here. PHP has a straight, down-to-earth character with very little magic going on. This is what makes the code easy to understand and debug. I just would not use the term 'syntactic sugar' in this context. To me, syntactic sugar has a positive connotation: A nicer way to express the same thing that is easier to read. Magic is making things happen in non-obvious ways that are easily missed when looking at the code. Magic can look really attractive, until something does not work as expected and you fail to see why. Python code tends to have tons of it. The point where sugar ends and magic begins is a matter of taste. To me, constructor promotion tends slightly towards magic. But then, anyone can choose to use the feature or not. > To be honest, my impression is that most of the problems you list (e.g. > verbose constructor, bean problem, > or even property accessors) mainly boil down to the verbosity of PHP (or > the "visual debt" problem > how some people calls it). As I wrote in the first paragraph, I don't think > it's a bad thing. Reducing verbosity is not the problem. Introducing magic is. While property accessors are magic, they are a form of magic that I would be willing to accept, for the following reasons. A nice feature of accessors is that they allow swapping a traditional public class property for a property accessor without changing API. Such a feature currently has little value, because public properties are not commonly used because they cannot be marked as read-only yet. Once public class properties can be marked read-only I would be comfortable exposing them directly, without writing any getters and setters for them. The only thing that would still worry me is: What if I need to add some form of access logic later on? Property accessors allow me to do this without introducing a BC break. So yes, property accessors are magic. However, in combination with read-only properties they would allow for dropping tons of getter methods and directly expose properties in stead. They allow this because they are the 'safety net' which makes me comfortable doing it. We get more PHP and less Java. This probably means that I will occasionally have to actually use property accessors at some point and introduce a bit of magic. Then, it still isn't the worst possible magic. Accessors are explicitly declared on the class, an editor could easily show me that accessing a particular property calls a method and show me the code. It's not ideal but it's not that bad either. > But I don't understand why is would be a good thing to have two types of > methods? How should we decide if we > should use normal methods or property accessors? I think the current I would prefer to only use accessors to add logic to property access without introducing a BC break. A successor to using __get() and __set(). For access logic that is obviously non-trivial from the start I would probably continue to use regular getter and setter methods. > I still don't think that property accessors would solve the main use-case > of "write-once" properties. Indeed. For that purpose I would prefer a keyword for marking a public property as read-only. Regards, Dik Takken -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
> > If the answer to that is "well don't do that", then what's the > alternative? PHP offers no other syntax for evolvable immutable objects > than private properties with Wither methods. Making Wither methods harder > makes evolvable immutable objects harder. Unless there's some entirely > different approach I am not aware of to achieve the same goal, in which > case please share. :-) > For transparency purposes: the idea I presented during the discussion of "write-once" properties was to make the following syntax (or a similar one) possible in order to make cloning of these properties possible: > $self = clone $this with {property1: "foo", ...}; It would clone the object and in the same time change the listed properties (no matter if they have the "write-once" flag). It would also take visibility rules into account, so a private property could only be modified in the private scope. I think this idea would address your concerns, although I haven't received any feedback about it yet, so I'm not sure if it has any gotchas/edge cases that would make it or its implementation infeasible. Máté
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
On Tue, Mar 24, 2020, at 6:52 AM, Nikita Popov wrote: > On Mon, Mar 23, 2020 at 1:48 AM Larry Garfield > Thanks for the write-up Larry. I like where you're going with this. > > If we were starting from a blank slate design, I would advocate for: > > a) having an object initializer syntax > b) not having first-class constructors at all > c) using named constructors instead. > > This also happens to be exactly what Rust does, go figure... Unfortunately > this kind of approach is hard to retrofit into PHP, because we already have > constructors, almost all classes define them, and it's hard to reconcile > object initialization syntax and non-trivial constructors in a meaningful > way. Combining this with the "no public properties" cargo cult we have > inherited from Java, the paradigm shift is probably too large here. If wishes were horses, I'd agree. Though both you and Mate mentioned named constructors; I am not sure how that would play into the compacted constructor here. > If we can't have object initializers, then improving what we can do with > constructors is the next best thing :) > > I generally like the ideal of combining property declaration and > constructors. I've had this on my mind for a while already, and also > received the same suggestion from a couple of other people (I think Nicolas > was one of them?) The current amount of boilerplate that is needed is just > large enough that I will often go with a quick and simple ad-hoc array > structure rather than declaring an explicit value object type. The main > concern, as others have already mentioned, is that these inline > declarations can end up being quite verbose, especially once attributes get > involved. > > I think I will write up a quick implementation & RFC for this part, as it > seems like something we should at least consider in more detail. I look forward to it! I am quite open to alternate syntaxes that are more amenable to attributes and delegation, as long as the net result is what we're after: Less repetition so making record-like classes is easier. > Named parameters are a pretty tough topic. I think one of the main points > of contention is that they make the parameters names part of the API > contract, and as such also subject to LSP. Your proposal offers two > possible ways to side-step this: First, by making named parameters opt-in > with a special syntax {}. Second, by limiting them to constructors. The > latter variant still exposes parameter names in the API, but at least does > not require their preservation across inheritance, as constructors are > excluded from LSP. I'm somewhat torn on this, because it makes named > parameters unusable with the very large body of existing methods, and > introduces an inconsistency in which methods can use named params and which > don't. My own feeling here is that making people care about parameter names is not actually that big of a deal. You should really be caring about variable names anyway. Python seems to do fine with named parameters being part of the contract AFAIK. Especially if we could get it in for PHP 8, that's a major anyway, so I would be fine with it. The other options (opt-in or constructor only) are IMO fallbacks in case we're nervous about it, or if the parser ends up being happier with a more explicit syntax. (Gotta keep the parser happy.) > Regarding the remainder, I think that all of readonly properties, > asymmetric visibility and property accessors have their place and value, > with some overlap between them. As you already mentioned, the previous > property accessors proposal also included asymettric visibility as a > special case, and that's how I would introduce it as well. > > However, I generally think that the main value really is the readonly > properties as proposed in the recent RFC. Nowadays, a large fraction of the > classes I use are immutable value objects, for which public readonly > properties provide a much closer match to the semantics I want. > > I think that the problem with with-er methods is just that: It's a problem > with with-er methods. It's what happens when you try to shove immutability > into something that is not actually being used in an immutable manner. > Don't pretend things are immutable when they aren't... > > Regards, > Nikita I agree that Withers are solving an odd problem; however, it's the same approach that PHP itself takes already. Consider DateTimeImmutable: $d = new DateTimeImmutable(); $d2 = $d->setDate(2020, 1, 3) ->setTime(12, 45) ->setTimezone(new DateTimeZone('America/Chicago') ->modify('+1 week'); That's a Wither pattern. The names a a bit wonky for compatibility with DateTime, but that's the exact approach that Wither methods model. From a user perspective it's pretty good. If the answer to that is "well don't do that", then what's the alternative? PHP offers no other syntax for evolvable immutable objects than private
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
On Mon, Mar 23, 2020 at 1:48 AM Larry Garfield wrote: > Hi folks. > > There have been a lot of RFCs and possible RFCs of late that are all > circling around the same related problem space: Working with objects right > now involves too much boilerplate to get things done. As I've mentioned > several times, I believe we need to be looking for broader solutions rather > than narrowly-focused one-offs. > > To that end, I have written an extensive analysis of the problem space and > the current and recent proposals. I've put it on my blog rather than > inline here because it's quite long and the blog offers better formatting. > > Discussion can happen here, but I'll also respond to comments there. > > In short: I believe our biggest potential win is to focus on 3 RFCs: > > * Constructor Promotion > * Named parameters > * Compound Property Visibility > > For details, see the full writeup: > > https://hive.blog/php/@crell/improving-php-s-object-ergonomics > > Thank you for your attention. > Thanks for the write-up Larry. I like where you're going with this. If we were starting from a blank slate design, I would advocate for: a) having an object initializer syntax b) not having first-class constructors at all c) using named constructors instead. This also happens to be exactly what Rust does, go figure... Unfortunately this kind of approach is hard to retrofit into PHP, because we already have constructors, almost all classes define them, and it's hard to reconcile object initialization syntax and non-trivial constructors in a meaningful way. Combining this with the "no public properties" cargo cult we have inherited from Java, the paradigm shift is probably too large here. If we can't have object initializers, then improving what we can do with constructors is the next best thing :) I generally like the ideal of combining property declaration and constructors. I've had this on my mind for a while already, and also received the same suggestion from a couple of other people (I think Nicolas was one of them?) The current amount of boilerplate that is needed is just large enough that I will often go with a quick and simple ad-hoc array structure rather than declaring an explicit value object type. The main concern, as others have already mentioned, is that these inline declarations can end up being quite verbose, especially once attributes get involved. I think I will write up a quick implementation & RFC for this part, as it seems like something we should at least consider in more detail. Named parameters are a pretty tough topic. I think one of the main points of contention is that they make the parameters names part of the API contract, and as such also subject to LSP. Your proposal offers two possible ways to side-step this: First, by making named parameters opt-in with a special syntax {}. Second, by limiting them to constructors. The latter variant still exposes parameter names in the API, but at least does not require their preservation across inheritance, as constructors are excluded from LSP. I'm somewhat torn on this, because it makes named parameters unusable with the very large body of existing methods, and introduces an inconsistency in which methods can use named params and which don't. Regarding the remainder, I think that all of readonly properties, asymmetric visibility and property accessors have their place and value, with some overlap between them. As you already mentioned, the previous property accessors proposal also included asymettric visibility as a special case, and that's how I would introduce it as well. However, I generally think that the main value really is the readonly properties as proposed in the recent RFC. Nowadays, a large fraction of the classes I use are immutable value objects, for which public readonly properties provide a much closer match to the semantics I want. I think that the problem with with-er methods is just that: It's a problem with with-er methods. It's what happens when you try to shove immutability into something that is not actually being used in an immutable manner. Don't pretend things are immutable when they aren't... Regards, Nikita
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
Hi Larry, In my opinion, one of the core assets of PHP is that it contains relatively few syntactic sugar compared to some other languages, e.g. C#. Maybe it's just me, but I believe it makes the code written in PHP easier to read. And I think this is what we should optimize for. Not for saving a few lines of code, but for making the code easier to read and understand. Now, if we had constructor promotion, we should search for properties both on the top of the class and in the constructor. And I agree with Michał, this kind of code can get out of hand of control very fast. That said, I don't think that declaring properties in the constructor is a good idea. It's also because many people (including myself) tend to write static methods first (I mainly use them as named constructors), so we'd either lose track of properties declared in the constructor or have to force a code style that puts the constructor to the top. Also, some IDEs (but PHPStorm for sure) can generate the constructor very easily from the declared properties. Speaking about the evaluation of "Write-Once Properties" and "Compound Property Visibility", I disagree in some regards. I'll start with the less important one: > Because the write-once state is preserved across cloning, it makes > Evolution worse. I think it's quite expected that properties won't be writable after cloning. That would be a very bad design otherwise. That's why I think your real problem is the opposite: that currently the clone operator is not prepared for this change. That's why I missed the "Rust-like cloning" (or the other clone variant that I presented in the "write-once property" thread) as the solution of the "Evolution" problem of "write-once" properties. My other problem with the evaluation of the "Immutability problem" is that it suggests that immutability is only an external concern, and it isn't a thing in the private/protected scope. Why do you think so? Currently (unfortunately) visibility is the only thing that can at some extent (in external scopes) control mutability in PHP. However, if we look at the problem from the type system perspective, visibility has nothing to do with it: we won't have any guarantee that a property is immutable even if we make it private. To be honest, my impression is that most of the problems you list (e.g. verbose constructor, bean problem, or even property accessors) mainly boil down to the verbosity of PHP (or the "visual debt" problem how some people calls it). As I wrote in the first paragraph, I don't think it's a bad thing. For example, if we had "compound property visibility" then we could separate read/write visibility of properties without using getters/setters (I think this is what you also wrote). If we had property accessors then besides the separation of visibility, we could have materalized properties or properties that validate themselves. Probably the syntax would become more concise, but effectively we would make methods from the properties. But I don't understand why is would be a good thing to have two types of methods? How should we decide if we should use normal methods or property accessors? I think the current situation is much better: use getters and/or setters to separate visibility of properties, and perform validation in setters if you need it. And I I still don't think that property accessors would solve the main use-case of "write-once" properties. Cheers, Máté
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
On Mon, Mar 23, 2020, at 2:47 PM, Michał Brzuchalski wrote: > Hi Larry, > > The value here is combining constructor promotion with named parameters. > > Constructor promotion itself is useful for the class implementer, but > > doesn't help the caller. Named parameters helps the caller, but doesn't > > really help the class implementer. The combination of both of them > > together gives us something similar to the object-initializer syntax as a > > net result, but with many other benefits because it's not a one-off syntax. > > > > So with the two of them together, you get: > > > > class Point { > > public function __construct({public int $x, public int $y}); > > } > > > > Which then allows any of these construction mechanisms: > > > > $p1 = new Point(5, 7); > > $p2 = new Point({x: 5, y: 7}); > > $p3 = new Point({y: 7, x: 5}); > > > > All of which result in an object you can use the same way: > > > > print $p1->x . ', '. $p1->y; > > > > > I agree it looks a little bit awkward and differs from object-initializers > known from other languages, > but let's say it would work somehow for this example. Now make it not 2 but > 10-15 properties > with real types sometimes quite long so after 3-5 of them you should break > the line, > then add some default values. > Like a real entity which with typed properties doesn't need setters and > getters. > > The example grows but even when breaking a line after each > parameter/property still could be somehow readable. > > Now as we deal with Entity add some annotations or let's go hype, try > with > new Attributes v2 > proposed by Benjamin Eberlei > https://wiki.php.net/rfc/attributes_v2#userland_use-casemigrating_doctrine_annotations_from_docblocks_to_attributes > 3 for $id and for the rest at least one attribute per property. > > class Product { > public function __construct({ > <> > <> > <> > public int $id, > > < true])>> > public string $name, > > <> > public string $description > }); > } > > Let's stop on 3 I think it's enough to see it's: > 1. unusual to see annotations in method signature declaration > 2. not readable anymore. > > Now if you say it shouldn't be like that and all the properties should be > declared as normal properties, > then the constructor is not needed to simplify the declaration but still > requires a lot of boilerplate on > the caller side undoubtedly. > > Do you still think object-initializer is pointless and useless and can be > replaced with > named arguments and constructor arguments promotion? > > Cheers, > Michał Yes, I responded to the annotations point in my earlier reply. I hadn't considered those, so we'll likely need to consider alternate syntaxes if we want both annotations and constructor promotion (which I do). However, note that even the (admittedly fugly) Product example you have above, each property name is listed only once, not 4 times, making it still an improvement on the status quo as far as redundancy. However, I still hold that constructor promotion and named parameters, taken together, are strictly superior to a one-off syntax for objects that only supports public properties, as they offer more capabilities and have less potential to be confused with future additions. Eg, object-initalizer syntax vs named parameters on a constructor; which to use when? Why? Why do both exist? They really shouldn't. Named params would be better than object-initalizers all on their own, regardless of whether constructor promotion is included, precisely because they offer more capability for the same syntactic addition. --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
Hi Larry, pon., 23 mar 2020 o 20:04 Larry Garfield napisał(a): > Merging some replies together here... > > On Sun, Mar 22, 2020, at 8:36 PM, Levi Morrison via internals wrote: > > > In short: I believe our biggest potential win is to focus on 3 RFCs: > > > > > > * Constructor Promotion > > I would vote yes on this, assuming the implementation is sane. > > On Mon, Mar 23, 2020, at 12:55 AM, Michał Brzuchalski wrote: > > > That still doesn't resolve issue with lot of boilerplate when you deal > with > > small objects for which public readonly is enough like object-initializer > > could. So I'm not sure about my vote here. It does solve only one narrow > > situation for me. > > The value here is combining constructor promotion with named parameters. > Constructor promotion itself is useful for the class implementer, but > doesn't help the caller. Named parameters helps the caller, but doesn't > really help the class implementer. The combination of both of them > together gives us something similar to the object-initializer syntax as a > net result, but with many other benefits because it's not a one-off syntax. > > So with the two of them together, you get: > > class Point { > public function __construct({public int $x, public int $y}); > } > > Which then allows any of these construction mechanisms: > > $p1 = new Point(5, 7); > $p2 = new Point({x: 5, y: 7}); > $p3 = new Point({y: 7, x: 5}); > > All of which result in an object you can use the same way: > > print $p1->x . ', '. $p1->y; > > I agree it looks a little bit awkward and differs from object-initializers known from other languages, but let's say it would work somehow for this example. Now make it not 2 but 10-15 properties with real types sometimes quite long so after 3-5 of them you should break the line, then add some default values. Like a real entity which with typed properties doesn't need setters and getters. The example grows but even when breaking a line after each parameter/property still could be somehow readable. Now as we deal with Entity add some annotations or let's go hype, try with new Attributes v2 proposed by Benjamin Eberlei https://wiki.php.net/rfc/attributes_v2#userland_use-casemigrating_doctrine_annotations_from_docblocks_to_attributes 3 for $id and for the rest at least one attribute per property. class Product { public function __construct({ <> <> <> public int $id, < true])>> public string $name, <> public string $description }); } Let's stop on 3 I think it's enough to see it's: 1. unusual to see annotations in method signature declaration 2. not readable anymore. Now if you say it shouldn't be like that and all the properties should be declared as normal properties, then the constructor is not needed to simplify the declaration but still requires a lot of boilerplate on the caller side undoubtedly. Do you still think object-initializer is pointless and useless and can be replaced with named arguments and constructor arguments promotion? Cheers, Michał
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
Merging some replies together here... On Sun, Mar 22, 2020, at 8:36 PM, Levi Morrison via internals wrote: > > In short: I believe our biggest potential win is to focus on 3 RFCs: > > > > * Constructor Promotion > I would vote yes on this, assuming the implementation is sane. On Mon, Mar 23, 2020, at 12:55 AM, Michał Brzuchalski wrote: > That still doesn't resolve issue with lot of boilerplate when you deal with > small objects for which public readonly is enough like object-initializer > could. So I'm not sure about my vote here. It does solve only one narrow > situation for me. The value here is combining constructor promotion with named parameters. Constructor promotion itself is useful for the class implementer, but doesn't help the caller. Named parameters helps the caller, but doesn't really help the class implementer. The combination of both of them together gives us something similar to the object-initializer syntax as a net result, but with many other benefits because it's not a one-off syntax. So with the two of them together, you get: class Point { public function __construct({public int $x, public int $y}); } Which then allows any of these construction mechanisms: $p1 = new Point(5, 7); $p2 = new Point({x: 5, y: 7}); $p3 = new Point({y: 7, x: 5}); All of which result in an object you can use the same way: print $p1->x . ', '. $p1->y; > > * Named parameters > This one is tricky -- there are tradeoffs of every approach I've seen > proposed. I have an idea for another way to address this space, but it > too has drawbacks; there's inherently no free lunch here, I think. Please share. I make no claim of being fully versed in the implementation details of how these would work at the engine level, only at the syntax level. > > * Compound Property Visibility > What is this last one? I searched for "compound" in the article, but > did not get many hits, none of which described what it actually _is_. Mm, yeah, I sorta changed names on that part way through. That's the "different visibility for read and for write" proposal that Nicolas mentioned in the readonly thread, for which I borrowed the syntax from the property accessors RFC. On Mon, Mar 23, 2020, at 1:07 AM, Mike Schinkel wrote: > 1. Your post mentions validation needed for value objects — a concern I > share — but AFAICT your conclusion does not address the issue. It doesn't directly in a syntactic way. Rather, it allows the constructor and Setter/Wither methods to still exist as now, and users can write what they want in there. That is, it's no change from now. That was one of my evaluation criteria: Improve as many of these as possible without making any of them worse.. This is one were "nothing is made worse". > 2. It also mentions overlapping concerns and references numerous RFCs, > but there were two other relative works not mentioned. One[1] is an RFC > and the other[2] a PR on Github. I think they both overlaps with this > problem space, with the former addressing validation whereas the latter > case potentially conflicts with constructor promotion. You are correct, I forgot about the impact of annotations (which I also support). Off hand, I think their only issue would be in relation to the constructor promotion, as the resulting syntax to put a bunch of annotations inside the constructor definition would get... silly. That suggest we may want a different syntax for constructor promotion than what I proposed we borrow from Hack, but I don't think changes the overall argument. > 1. You don't really address the value object vs. service object > distinction, except on the periphery. Correct. This also ties into your discussion of Structs/Records as an alternative approach. I have considered that before, but in this writeup... I found nowhere that any of the possible implementations would be useful only for one or the other. Rather, certain features may be more useful to one or the other but would definitely have uses in both. At the end of the day, I think the only language-level difference between a value and service object is the passing semantics; service objects should pass as they do now, while value objects would, ideally, pass in a way more similar to arrays. However, that was off topic at this time, and not needed. Everything else discussed would be applicable to both object types, so splitting the syntax would not be helpful, just confusing. For instance, property accessor methods on a new Struct type would have the exact same performance issue as they would on a Class today. The goal was to focus on the minimum amount of work we can do to get the maximum benefit, and I don't think a new Struct type would qualify for that. > 2. You mention the concerns about exposing parameter names as part of > the API but don't address those concerns directly. I do. Specifically, making named parameters opt-in allows class/function/method authors to decide if they
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
> On Mar 22, 2020, at 8:47 PM, Larry Garfield wrote: > https://hive.blog/php/@crell/improving-php-s-object-ergonomics Hi Larry, That is a really excellent writeup. Thanks from me at least for taking the time to write it up in depth. Looking at your conclusion, my gut feelings tell me your conclusion is about 85% there, but with about 15% that gives me pause unless addressed, which I think is imminently possible. I'm going to do my best to explain what I am envisioning but realize I had not reserved time for this today so I am rushing to complete before my Monday starts in too few hours. Given that I will almost certainly not be as clear in my thoughts had I had time to write and review it so please ask for clarification if something does not make sense. --- 1. Your post mentions validation needed for value objects — a concern I share — but AFAICT your conclusion does not address the issue. 2. It also mentions overlapping concerns and references numerous RFCs, but there were two other relative works not mentioned. One[1] is an RFC and the other[2] a PR on Github. I think they both overlaps with this problem space, with the former addressing validation whereas the latter case potentially conflicts with constructor promotion. 3. And finally, some of the proposed concepts add syntax elements but do not make them first class language elements. --- But before I cover those let me cover what I think you nailed to perfection: 1. The need resolve the verbosity of day-to-day programming with classes on PHP. 2. The distinction between Service and Value objects. 3. The value of immutability. 4. The downside of using arrays to pass properties (which is how many of us do it today.) 5. The benefits of contextual access 6. The limited nature of COPA/and write-once properties. 7. The elegance of property accessor syntax and how it can be applied to create readonly properties 8. The elegance of named parameters with the distinction between BC parameter passing and a newer JSON object-like syntax. 9. Rust-like cloning/Construct from syntax. 10. The problem of exposing parameter names as part of an external API for existing code 11. The problems associated with get and set properties actually running code when not expected by the user. --- However, here is were I think your proposal still needs tightening up. 1. You don't really address the value object vs. service object distinction, except on the periphery. 2. You mention the concerns about exposing parameter names as part of the API but don't address those concerns directly. 3. You mention get/set properties surprisingly running code but do not address those concerns directly. 4. Your concept for a JSON object-like syntax for passing parameter feels incomplete to me. Unless I miss understand it is just a special syntax that only works in the context of passing arguments to a constructor, method or function, and not a first-class language element. If we were to go that route I think we would find it highly limiting. 5. In the section you say that "either of the following construction styles becomes possible" but you do not talk about the option of having one of more of the first parameters being positional and the rest being able to be passed by name, which I think would be an ideal use-case when you want to force certain parameters to always be passed but make the rest optional. --- 1. Let me now address the conflict with Nikita's "decorator" pattern support PR (which I and several others think would be better called delegation.) (As an aside, Go has such a feature and after having used it I find working with PHP's lack of delegation to be extremely painful. Well at least we have Traits. You can see my comment on that PR and my proposal here[3] and [4].) I think that delegation addresses the no-win scenario of and thus is an extremely important addition not to block: - "Should I extend a class and have fragile base classes?" - "Should I use containment and then have a nightmare of boilerplate?", or - "Should I use magic methods and loose performance and native ability use reflection and *_exists() functions?" Unfortunately I think that Constructor Promotion assumes that all properties will be associated with the class at hand, and not with classes that are being delegated to. Yes we could assume they are joined together, and distributed to the various delegated instances, but I believe that could get very complicated very quickly. 2. Going further with constructor promotion, which you join with named parameters, how are the parameters represented inside the constructor/method/function? As different variables methods just like current parameters? Is there no concept of automatic aggregation of those parameters into some kind of structure that could then be passed down to other methods and functions? 3. And — I don't want to bike-shed — but I think the proposed constructor promotion syntax
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
Hi Larry, pon., 23 mar 2020, 01:48 użytkownik Larry Garfield napisał: > Hi folks. > > There have been a lot of RFCs and possible RFCs of late that are all > circling around the same related problem space: Working with objects right > now involves too much boilerplate to get things done. As I've mentioned > several times, I believe we need to be looking for broader solutions rather > than narrowly-focused one-offs. > > To that end, I have written an extensive analysis of the problem space and > the current and recent proposals. I've put it on my blog rather than > inline here because it's quite long and the blog offers better formatting. > > Discussion can happen here, but I'll also respond to comments there. > > In short: I believe our biggest potential win is to focus on 3 RFCs: > > * Constructor Promotion > That still doesn't resolve issue with lot of boilerplate when you deal with small objects for which public readonly is enough like object-initializer could. So I'm not sure about my vote here. It does solve only one narrow situation for me. * Named parameters > What would be nice for methods and functions. +1 * Compound Property Visibility > I didn't get it what benefits over property accessors it could have. > For details, see the full writeup: > > https://hive.blog/php/@crell/improving-php-s-object-ergonomics > > Thank you for your attention. > > -- > Larry Garfield > la...@garfieldtech.com > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis
> In short: I believe our biggest potential win is to focus on 3 RFCs: > > * Constructor Promotion I would vote yes on this, assuming the implementation is sane. > * Named parameters This one is tricky -- there are tradeoffs of every approach I've seen proposed. I have an idea for another way to address this space, but it too has drawbacks; there's inherently no free lunch here, I think. > * Compound Property Visibility What is this last one? I searched for "compound" in the article, but did not get many hits, none of which described what it actually _is_. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php