'Uninitialized' creates a lot of cluttering

php.internals

Lydia de Jongh

3 years ago
Hi, After posting this in the issues list <https://github.com/php/php-src/issues/10537>, I was asked to mail it here. This is my first time here, so I hope I do it the right way. ------------------------------- At last I have the opportunity to start using php8, I am very happy with it and how mature it has become; implementing all kinds of code quality. But this new *uninitialized state* is really a pain. When you set a property in a class, I consider it as set into the class; it is part of that class, that object. Whether it is undefined or not. You could access it with an easy if or ternary operator. And you got a *clean overview* of all your properties and how they are initialized. class Test { protected string $name; protected string $type = 'mytype'; protected ?array $arrLog; protected ?string $optionalVar; protected string $table = 'category'; protected bool $isOk = false; } *This looks clean and gives clear, easy insight* to the purpose of each property. But since php8, you have to use isset() or empty() to check whether a property is initialized. Also php is offering more and more shorthand alternatives like nullsafe operator, ternary operator etc. Which I was not in favor of, but it seems to be the standard nowadays, so I let phpstorm do its thing and use the shorthands. But when you start using the (very necessary!!!) typed properties this is not working anymore unless you 'initialize' the property with at least an empty or dummy value or null. I read the RFC trying to understand..... Maybe it is technically difficult or it is against high-standard rules (which I love!), but this is really unpractical and gives to much overhead and clutter. I love good quality in coding, I endured years of being called crazy by colleagues who learned php by google and stackoverflow and loved it because it allowed all the bad coding. (Good is good enough) But now it seems to go over the top, making php less practical. For this uninitialized-thing we can choose one the following solutions at the moment: - leave properties untyped, which is really bad - set many properties to null or other empty values, which clutters your property list - add those empty values inside the constructor, which creates useless extra lines of code - use isset or empty to check if it is set or not, and not be able to use shorthands To use typed properties and profit from all the new shorthands and nice new feature of php 8, my class has to be something like: class Test { protected string $name = ''; protected string $type = 'mytype'; protected ?array $arrLog = []; protected ?string $optionalVar = null; protected string $table = 'category'; protected bool $isOk = false; } This looks cluttered. And I do not understand that we get all those nice shorthands, but for typed properties, we need to type more.... Why does a nullable type not automaticly default to null, or even to the empty variant of the type (by giving it not an question-mark but the exclamation mark, maybe...) like: class Test { protected !string $name; // defaults to '' protected string $type = 'mytype'; protected !array $arrLog; // defaults to [] protected ?string $optionalVar; // defaults to null protected string $table = 'category'; protected !bool $isOk; // defaults to false protected !float $someNumber; // default to 0 protected ?myObject $oObject; // no default for objects... to complicated and risky, or not??, so only nullable } I hope this can be considered again. Greetz, flexJoly (Lydia de Jongh) https://www.linkedin.com/in/flexjoly/ https://divaeloper.wordpress.com/

Kamil Tekiela

3 years ago
Hi Lydia, I understand where you are coming from because I encountered this a few times myself, but every time it was actually because I was doing something wrong. The reason for this limitation is actually very simple and logical. Let me explain. When you use untyped properties, the type is not restricted so every property can be nullable by default. It's very simple for the language to have NULL as the default value for all untyped properties. When using typed properties, the language cannot use NULL as the default anymore because the type might not allow NULL, e.g public string $name allows only string values. Unless you assign some string value to it, the language will not initialize it with any value. If you need the property to have a state indicating no value, you can make it nullable and assign NULL as the default value. What you are proposing is actually not acceptable from the type system's point of view. We cannot add a new syntax for assigning default values to type properties. It might make sense in a very limited scope, e.g. public ! int $number; defaults to 0, but in any other more complex scenario, this fails miserably. Consider this example: class Test { public ! MyObject&SomeInterface $prop1; public ! int|false|null $prop2; } In the example above, what default values could be assigned to the properties? The correct answer is none. You cannot have a default object, nor can you pick a default value from the union of three types. There is simply no way to determine the default value. So if we had a new syntax like this, it would be severely limited to only the simplest scenarios. This would create a new inconsistency for the benefit of using a single character instead of assigning the value. There would be no benefit to introducing such inconsistency to the language. It is the job of the developer to specify the default type. Some things can be left for PHP to infer, but when it comes to the default property value, the developer needs to assign it. You said that an isset is necessary when using typed properties. I would argue that an isset is not needed. Using isset with typed properties is a code smell in many circumstances. If the property can be unassigned, the developer should use a sentinel value such as null to indicate this. If the property is always expected to hold a value, then the code should not allow a path to access it before it's initialized. Kind Regards, Kamil

Unnamed Person

3 years ago
Am 08-Feb-2023 15:59:02 +0100 schrieb tekiela246@gmail.com: > When using typed properties, the language cannot use NULL as the default > anymore because the type might not allow NULL, e.g public string $name > allows only string values. Would it make sense to make "null" the default value for nullable properties at least? So that one could write class Test { public ?string $name; } var_dump((new Test())->name); // null Best regards Christian

Tim Düsterhus

3 years ago
Hi On 2/8/23 17:04, naitsirch@e.mail.de wrote:
> Am 08-Feb-2023 15:59:02 +0100 schrieb tekiela246@gmail.com: >> When using typed properties, the language cannot use NULL as the default >> anymore because the type might not allow NULL, e.g public string $name >> allows only string values. > > Would it make sense to make "null" the default value for nullable properties at least? > So that one could write > > class Test { > public ?string $name; > } > > var_dump((new Test())->name); // null >
No, I find the difference between "null" and "uninitialized" useful, because it makes the behavior explicit. In case I make a mistake and accidentally don't assign a value to the property when I should've, perhaps I've forgot to call the necessary setter in my constructor. If I later access the property it will blow up instead of silently feeding me garbage data. Adding special logic for nullable properties to save the developer from typing the 7 characters '= null;' in some rare cases, does not sound useful to me. Best regards Tim Düsterhus

Kamil Tekiela

3 years ago
> But because of the typed properties, you have to initialize them before
you can access them. I think that is cluttering up the code. It's not cluttering code. You don't need isset if you do it properly. You have a bug if your code tries to access an uninitialized property.
> From a programmer's perspective, the property is declared and should be
accessible. Even if it has no value. There should be no difference between typed and none-typed properties for this. You cannot fetch value of something that has no value. The property is declared, but the value was never set. There is a difference because, as I explained it earlier, untyped properties were implicitly nullable and had a default null value. Typed properties do not have an implicit default value, and they do not hold any value until the programmer assigns some value. The property is accessible, but it doesn't have any value, not even null value.

Unnamed Person

3 years ago
Am 08-Feb-2023 17:34:43 +0100 schrieb tim@bastelstu.be: > Hi > > On 2/8/23 17:04, naitsirch@e.mail.de wrote: > > Am 08-Feb-2023 15:59:02 +0100 schrieb tekiela246@gmail.com: > >> When using typed properties, the language cannot use NULL as the default > >> anymore because the type might not allow NULL, e.g public string $name > >> allows only string values. > > > > Would it make sense to make "null" the default value for nullable properties at least? > > So that one could write > > > > class Test { > > public ?string $name; > > } > > > > var_dump((new Test())->name); // null > > > > No, I find the difference between "null" and "uninitialized" useful, > because it makes the behavior explicit. > > In case I make a mistake and accidentally don't assign a value to the > property when I should've, perhaps I've forgot to call the necessary > setter in my constructor. If I later access the property it will blow up > instead of silently feeding me garbage data. That's a valid point. Thanks for the hint. > > Adding special logic for nullable properties to save the developer from > typing the 7 characters '= null;' in some rare cases, does not sound > useful to me. > > Best regards > Tim Düsterhus Best regards Christian

Lydia de Jongh

3 years ago
Hi, Thanks for all the answers so far. On 2/8/23 17:04, naitsirch@e.mail.de wrote:
> > Would it make sense to make "null" the default value for nullable
properties at least?
> > So that one could write
> >
> > class Test {
> > public ?string $name;
> > }
> >
> > var_dump((new Test())->name); // null >
Yes, this would help a lot and is exactly what I am asking for. Then the difference between typed and none-typed properties would also disappear. Making it more favorable to use the typed ones. Op wo 8 feb. 2023 om 17:34 schreef Tim Düsterhus <tim@bastelstu.be>:
> >
No, I find the difference between "null" and "uninitialized" useful,
> because it makes the behavior explicit. >
In case I make a mistake and accidentally don't assign a value to the
> property when I should've, perhaps I've forgot to call the necessary > setter in my constructor. If I later access the property it will blow up > instead of silently feeding me garbage data.
I think this should not be an argument on this level. The features of a programming-language should not be about preventing mistakes, imho. Else would it be possible to make a php.ini setting for this: only_explicit_property_init = true|false? Adding special logic for nullable properties to save the developer from
> typing the 7 characters '= null;' in some rare cases, does not sound > useful to me.
For me it is about the cluttering in an otherwise clean property list. On the other hand, so many new features in php8 are about less coding. Shorter ifs, nullable operators, setting properties directly in constructor..... For a long time I refused to use those, and used the longer typing, because it is seen better, more explicit 😇 and so gives less errors. $oObject?->test() is much easier overlooked then: if($oObject){ $oObject->test(); } So I do not understand why things like this may not be about less coding. Op wo 8 feb. 2023 om 19:12 schreef Rowan Tommins <rowan.collins@gmail.com>:
> > I've actually been considering a proposal to remove this difference *the > other way around*: if a property is declared but never assigned a value, > consistently giving an error on access, regardless of whether a type was > declared or not. > >> > Currently, properties can be in a number of different states: declared and > assigned, undeclared, undeclared but created dynamically by assignment, > declared without type and not yet assigned, declared with type and not yet > assigned (Uninitialized), declared without type but then unset (distinct > from both the unassigned and Uninitialized states) ... possibly other > combinations I've forgotten. >
Lol, sounds horrible indeed!
> > Now that we have the Uninitialized state, and have deprecated dynamic > properties, this could mostly be reduced to two: has a current valid value, > or Uninitialized. But the details of what would need to change and when are > the subject for a future discussion. >
Sounds much better! And I think that implicit null on nullable properties could fit nicely here. For null is a valid value for a nullable. So I still would like to ask if you can reconsider this. nb. Maybe I should introduce myself a little bit (more): I have been a php-programmer for about 20 years now, seen php4, 5, 7 and now 8. I did my study on Ambi Cobol to be an application programmer, where I learned very strict coding. From my ex (java architect) I learned oop. I have used these programming rules/techniques in php even when it was not required. So it is not that I want php to be less strict!!! (just to be sure 😝) Greetz, Lydia

Tim Düsterhus

3 years ago
Hi On 2/12/23 12:11, Lydia de Jongh wrote:
> In case I make a mistake and accidentally don't assign a value to the >> property when I should've, perhaps I've forgot to call the necessary >> setter in my constructor. If I later access the property it will blow up >> instead of silently feeding me garbage data. > > > I think this should not be an argument on this level. > The features of a programming-language should not be about preventing > mistakes, imho.
Why not? The primary (and possibly only) reason for an expressive type system is preventing mistakes from happening and thus cutting down the time spent writing tests and debugging. Preventing as many (expensive) mistakes from happening as possible is also one of the selling points of modern programming languages like Rust.
> Else would it be possible to make a php.ini setting for this: > only_explicit_property_init = true|false?
New php.ini settings that change the behavior of code depending on where it's run are generally ill-received nowadays. Library authors would need to take care to support all possible options for the setting, which in this case would effectively mean making all initialization explicit, nullifying whatever benefit the option would bring in the first place.
> Adding special logic for nullable properties to save the developer from >> typing the 7 characters '= null;' in some rare cases, does not sound >> useful to me. > > > For me it is about the cluttering in an otherwise clean property list. > > On the other hand, so many new features in php8 are about less coding. > Shorter ifs, nullable operators, setting properties directly in > constructor..... > For a long time I refused to use those, and used the longer typing, because > it is seen better, more explicit 😇 and so gives less errors. > > $oObject?->test() > is much easier overlooked then: > > if($oObject){ > > $oObject->test(); > > } > > So I do not understand why things like this may not be about less coding.
I'm not sure the comparison holds. Staying with your example for the null-safe operator: Using the null-safe operator is still explicit about the developer's intent: The question-mark makes it clear that the developer thought about the fact that the object variable might be null and that short-circuiting to null is the right thing to do. Allowing leaving out a default value for the property can mean both "I explicitly want it to be null" and "I forgot to set the property". Whereas with the current situation of "uninitialized", the former is explicit and the latter will result in an easily detectable error, instead of silently continuing execution with garbage data. Best regards Tim Düsterhus

Rowan Collins

3 years ago
On 12 February 2023 11:11:31 GMT, Lydia de Jongh <flexjoly@gmail.com> wrote:
>The features of a programming-language should not be about preventing >mistakes, imho.
What is the point of marking the type of a property, other than to prevent mistakes?
>For me it is about the cluttering in an otherwise clean property list.
It's only "clutter" if you don't think it conveys useful information, and that's obviously a matter of opinion.
>And I think that implicit null on nullable properties could fit nicely >here. For null is a valid value for a nullable.
For a type of ?int, null is indeed a valid value; but so is 0, and -1, and so on. Why should the language assume that one default, among all the possibilities, if you don't specify any? Regards,
-- Rowan Tommins [IMSoP]

Robert Landers

3 years ago
On Sun, Feb 12, 2023 at 6:47 PM Rowan Tommins <rowan.collins@gmail.com> wrote:
> > On 12 February 2023 11:11:31 GMT, Lydia de Jongh <flexjoly@gmail.com> wrote: > >The features of a programming-language should not be about preventing > >mistakes, imho. > > > What is the point of marking the type of a property, other than to prevent mistakes? > > > >For me it is about the cluttering in an otherwise clean property list. > > > It's only "clutter" if you don't think it conveys useful information, and that's obviously a matter of opinion. > > > >And I think that implicit null on nullable properties could fit nicely > >here. For null is a valid value for a nullable. > > > For a type of ?int, null is indeed a valid value; but so is 0, and -1, and so on. Why should the language assume that one default, among all the possibilities, if you don't specify any? > > > Regards, > > -- > Rowan Tommins > [IMSoP] > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php >
> What is the point of marking the type of a property, other than to prevent mistakes?
In non-strict mode, it coerces quite nicely. For example, a string to an integer or an integer to a string. These aren't "mistakes" but making use of the language features.
> For a type of ?int, null is indeed a valid value; but so is 0, and -1, and so on. Why should the language assume that one default, among all the possibilities, if you don't specify any?
I hope we can all agree that `null` is the absence of a value. Now we currently have two different meanings of "absence of a value" which is super annoying sometimes.

Andreas Heigl

3 years ago
Hey all. On 13.02.23 13:12, Robert Landers wrote: [...]
>> What is the point of marking the type of a property, other than to prevent mistakes? > > In non-strict mode, it coerces quite nicely. For example, a string to > an integer or an integer to a string. These aren't "mistakes" but > making use of the language features. > >> For a type of ?int, null is indeed a valid value; but so is 0, and -1, and so on. Why should the language assume that one default, among all the possibilities, if you don't specify any? > > I hope we can all agree that `null` is the absence of a value. Now we > currently have two different meanings of "absence of a value" which is > super annoying sometimes.
We have two different "things" that currently return a NULL value. Similar to a function with a void returntype still returning NULL. Perhaps we need to think about introducing "undefined" to make that clearer? But what is actually clearer than throwing an error when trying to access something that is undefined... https://stackoverflow.com/questions/5076944/what-is-the-difference-between-null-and-undefined-in-javascript So how complicated would it be to throw that same error when assigning the return type of a void function to a variable? Cheers Andreas
-- ,,, (o o) +---------------------------------------------------------ooO-(_)-Ooo-+ | Andreas Heigl | | mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" | | https://andreas.heigl.org | +---------------------------------------------------------------------+ | https://hei.gl/appointmentwithandreas | +---------------------------------------------------------------------+ | GPG-Key: https://hei.gl/keyandreasheiglorg | +---------------------------------------------------------------------+

Claude Pache

3 years ago
> Le 13 févr. 2023 à 13:12, Robert Landers <landers.robert@gmail.com> a écrit : > > I hope we can all agree that `null` is the absence of a value. Now we > currently have two different meanings of "absence of a value" which is > super annoying sometimes. >
Although `null` is often used with that semantics in mind, from a technical point of view, it is definitely not the same thing as “absence of a value”. For example: ```php function dump_answer($answer = 42) { var_dump($answer); } // null dump_answer(null); // NULL // absence of value dump_answer(); // int(42) ``` Or: ```php $a = [ 'foo' => null ]; // has value `null` var_dump(array_key_exists('foo', $a)); // true // is absent var_dump(array_key_exists('bar', $a)); // false ``` —Claude

Lydia de Jongh

3 years ago
Hi Kamil, Thanks for your reply. With regards to the automatic default, of course I can only agree! Indeed isset() should not be needed for declared properties! But because of the typed properties, you have to initialize them before you can access them. I think that is cluttering up the code. From a programmer's perspective, the property is declared and should be accessible. Even if it has no value. There should be no difference between typed and none-typed properties for this. Greetz, Lydia Op wo 8 feb. 2023 om 15:58 schreef Kamil Tekiela <tekiela246@gmail.com>:

Rowan Collins

3 years ago
On 8 February 2023 16:14:07 GMT, Lydia de Jongh <flexjoly@gmail.com> wrote:
>From a programmer's perspective, the property is declared and should be >accessible. Even if it has no value. >There should be no difference between typed and none-typed properties for >this.
I've actually been considering a proposal to remove this difference *the other way around*: if a property is declared but never assigned a value, consistently giving an error on access, regardless of whether a type was declared or not. Currently, properties can be in a number of different states: declared and assigned, undeclared, undeclared but created dynamically by assignment, declared without type and not yet assigned, declared with type and not yet assigned (Uninitialized), declared without type but then unset (distinct from both the unassigned and Uninitialized states) ... possibly other combinations I've forgotten. Now that we have the Uninitialized state, and have deprecated dynamic properties, this could mostly be reduced to two: has a current valid value, or Uninitialized. But the details of what would need to change and when are the subject for a future discussion. Regards,
-- Rowan Tommins [IMSoP]

Mike Schinkel

3 years ago
> On Feb 8, 2023, at 9:22 AM, Lydia de Jongh <flexjoly@gmail.com> wrote: > > Hi, > > After posting this in the issues list > <https://github.com/php/php-src/issues/10537>, I was asked to mail it here. > This is my first time here, so I hope I do it the right way. > > ------------------------------- > > At last I have the opportunity to start using php8, I am very happy with it > and how mature it has become; implementing all kinds of code quality. > > But this new *uninitialized state* is really a pain. > When you set a property in a class, I consider it as set into the class; it > is part of that class, that object. > Whether it is undefined or not. You could access it with an easy if or > ternary operator. > > And you got a *clean overview* of all your properties and how they are > initialized. > > class Test { > > protected string $name; > > protected string $type = 'mytype'; > > protected ?array $arrLog; > > protected ?string $optionalVar; > > protected string $table = 'category'; > > protected bool $isOk = false; > } > > *This looks clean and gives clear, easy insight* to the purpose of each > property. > But since php8, you have to use isset() or empty() to check whether a > property is initialized. > > Also php is offering more and more shorthand alternatives like nullsafe > operator, ternary operator etc. Which I was not in favor of, but it seems > to be the standard nowadays, so I let phpstorm do its thing and use the > shorthands. > > But when you start using the (very necessary!!!) typed properties this is > not working anymore unless you 'initialize' the property with at least an > empty or dummy value or null. > I read the RFC trying to understand..... > > Maybe it is technically difficult or it is against high-standard rules > (which I love!), but this is really unpractical and gives to much overhead > and clutter. > I love good quality in coding, I endured years of being called crazy by > colleagues who learned php by google and stackoverflow and loved it because > it allowed all the bad coding. (Good is good enough) > > But now it seems to go over the top, making php less practical. > > For this uninitialized-thing we can choose one the following solutions at > the moment: > > - leave properties untyped, which is really bad > - set many properties to null or other empty values, which clutters your > property list > - add those empty values inside the constructor, which creates useless > extra lines of code > - use isset or empty to check if it is set or not, and not be able to > use shorthands > > To use typed properties and profit from all the new shorthands and nice new > feature of php 8, my class has to be something like: > > class Test { > > protected string $name = ''; > > protected string $type = 'mytype'; > > protected ?array $arrLog = []; > > protected ?string $optionalVar = null; > > protected string $table = 'category'; > > protected bool $isOk = false; > } > > This looks cluttered. And I do not understand that we get all those nice > shorthands, but for typed properties, we need to type more.... > > Why does a nullable type not automaticly default to null, or even to the > empty variant of the type (by giving it not an question-mark but the > exclamation mark, maybe...) like: > > class Test { > > protected !string $name; // defaults to '' > > protected string $type = 'mytype'; > > protected !array $arrLog; // defaults to [] > > protected ?string $optionalVar; // defaults to null > > protected string $table = 'category'; > > protected !bool $isOk; // defaults to false > > protected !float $someNumber; // default to 0 > > protected ?myObject $oObject; // no default for objects... to > complicated and risky, or not??, so only nullable > } > > I hope this can be considered again.
Hi Lydia, I personally appreciate your concern. I program mostly in GoLang these days, its variables and struct properties are initialized to a "zero" value by default, and that works extremely well and cuts down on an entire class of errors. However, I know this is not what you wanted — and it is likely you already know how to do this — but in case this workaround had not already occurred to you the following is a proof-of-concept that might reduce the annoyance of having to initialize all your typed properties, at least somewhat, given that the list does not appear keen to address your concerns. This workaround uses a base `AutoInit` class with a constructor that uses reflection to inspect the properties and if they are uninitialized and without a default get their type and then initialize them to an appropriate "zero" value. Yes it uses reflection which is a bit slow, but you may or may not find that the performance drain a concern, given your use-cases. Also, it is just a proof-of-concept, so you may end up needing to fix edge-case bugs or tweak for your use-cases. Hope this helps. -Mike P.S. This workaround is also a great use-case for a feature I have always wanted in PHP which is the ability for a parent constructor to always be called if not explicitly specified otherwise. Too bad for this workaround that that feature does not already exist. <?php class AutoInit { function __construct() { $reflector = new \ReflectionObject($this); foreach($reflector->getProperties() as $prop) { if ($prop->isInitialized($this)) { continue; } if ($prop->hasDefaultValue()) { continue; } $type = $prop->getType(); if ($type->allowsNull()) { $type = "null"; } else { $type = $type->getName(); } switch ($type){ case "string": $value = ""; break; case "array": $value = []; break; case "bool": $value = false; break; case "float": $value = 0.0; break; case "int": $value = 0; break; case "null": $value = NULL; break; default: continue 2; } $prop->setAccessible(true); $prop->setValue($this,$value); } } } class Test extends AutoInit { protected string $name; protected int $age; protected float $latitude; protected float $longitude; protected string $type = 'unknown'; protected ?array $arrLog; protected ?string $optionalVar; protected string $table; protected bool $isOk; protected array $labels; function __construct() { parent::__construct(); } } $t = new Test(); var_export($t); // END