On 18/05/2024 11:52, Luigi Cardamone wrote:
I am already using a solution like the one
proposed by Robert: it works but it
leaves some doubts since each project
can have a different name for NotSet
An argument is often made that this is a good thing, in the sense that
"null" and other "universal" terminal values combine multiple meanings
in an unhelpful way. You'll see this frequently regarding SQL's handling
of NULL, for instance - does it mean "unknown", "not applicable",
"invalid", etc.
A common solution put forward to this perceived problem is "algebraic
data types" - a Maybe or Option type for "value or missing", an Error or
Failable type for "value or error", etc. PHP doesn't have those
(yet...), but the same information can be conveyed nicely with a final
class or single-element enum.
In your example, the actual value you want to represent is not "Not
Set", it's "Keep Current Value", so that's the terminal value you need:
final class KeepCurrent {}
class MyDTOPatch {
public int|null|KeepCurrent $propA;
public int|null|KeepCurrent $propB;
}
or:
enum PatchState { case KeepCurrent }
class MyDTOPatch {
public int|null|PatchState $propA;
public int|null|PatchState $propB;
}
Are there any downsides in adding a
specific syntax to check if a property
is initialized with any value?
In my opinion - and I stress that others may not share this opinion -
the entire concept of "uninitialized properties" is a wart on the
language, which we should be doing our best to eliminate, not adding
more features around it.
As a bit of background, the concept was created when typed properties
were being added, to handle a limitation of the language: given the
declaration "public Foo $prop;" there is no way to specify an initial
value which meets the type constraint. For nullable properties, you can
write "public ?Foo $prop=null;" but since PHP (thankfully) distinguishes
nullable and non-nullable types, you can't write "public Foo $prop=null;"
Some languages, e.g. Swift, require that all properties are initialised
before the constructor returns, but retrofitting this to PHP was
considered impractical, so instead it was left to a run-time error: if
you fail to initialise a property, you will get an error trying to
access it.
To track that, the engine has to record a special state, but assigning a
meaning to that error state is a bit like using exceptions for flow
control. It would be more in keeping with the original purpose to have
an object_is_valid() function, which returned false if *any* property
had not been initialised to a valid value.
PHP actually has a bewildering variety of such special states. In a
different compromise added at the same time, calling unset() on a typed
property puts it into a *separate* state where magic __get and __set are
called, which they are not if the property has simply not yet been
assigned, e.g. https://3v4l.org/C7rIF
I have always found this a mess. If a property says it is of type
"?int", I want to know that it will always be an integer or null, not
"int or null or uninitialised or unset". If it needs more than one
non-integer state, that should be specified in the type system, e.g.
"int|NotApplicable|NotSpecified|NotLoaded".
PS: Etiquette on this list is to post replies below the text you're
replying to, preferably editing to the relevant parts as I've done here,
rather than adding your text above the quoted message.
Required,
--
Rowan Tommins
[IMSoP]