Re: [PHP-DEV] Improving PHP's Object Egonomics: A broad analysis

2020-03-25 Thread Jakob Givoni
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

2020-03-25 Thread Dennis Birkholz
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

2020-03-25 Thread Larry Garfield
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

2020-03-25 Thread Nicolas Grekas
> > 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

2020-03-25 Thread Peter Bowyer
>
> 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

2020-03-24 Thread Dik Takken
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

2020-03-24 Thread Máté Kocsis
>
> 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

2020-03-24 Thread Larry Garfield
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

2020-03-24 Thread Nikita Popov
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

2020-03-24 Thread Máté Kocsis
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

2020-03-23 Thread Larry Garfield
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

2020-03-23 Thread Michał Brzuchalski
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

2020-03-23 Thread Larry Garfield
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

2020-03-23 Thread Mike Schinkel
> 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

2020-03-22 Thread Michał Brzuchalski
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

2020-03-22 Thread Levi Morrison via internals
> 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