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]

Reply via email to