On Sat, Jul 14, 2018 at 3:09 PM, Rowan Collins <rowan.coll...@gmail.com>
wrote:

> Hi all,
>
> As briefly mentioned, I think the approach to non-nullable types in the
> typed properties proposal needs more discussion, to make sure we're not
> repeating Tony Hoare's "billion-dollar mistake".
>
> For parameter hints, nullability is naturally optional, particularly in
> PHP where "nullable Foo" is equivalent to a union type "null or Foo". For
> properties, this isn't the case, because every property must have some
> initial state.
>
> The simplest solution, which I think would be a sensible starting point if
> we're not ready for more complex changes, is to say that all typed
> properties must have a default value, and if that default value is null,
> the type hint must naturally be nullable. In other words, make "public Foo
> $foo" illegal, but allow "public ?Foo $foo=null".
>
> The current RFC proposes the next simplest solution, which is to allow
> non-nullable types, and trust the user to initialise them before use.
>
> My first objection to this is that it creates an entirely new state for
> object properties to be in, which behaves differently from everything else
> in the language. There will then be three or four different ways for an
> object property to be "unset":
>
> - not declared at all; access gives a notice and the implicit value null
> - declared with no default, uninitialised, implicitly null
> - declared with a default of null, or explicitly assigned as null
> - declared with a non-nullable type, never initialised; rather than an
> implicit null, access generates an Error
>
> The bigger problem is that the properties are non-nullable in name only,
> and the declaration can't be trusted. Consider the following class:
>
> class Validity {
>     public \DateTimeInterface $validFrom;
>     public ?\DateTimeInterface $validTo = null;
> }
>
> If I have an instance $v of that class, I would expect to be able to
> safely call $v->validFrom->getTimestamp(), but need to check for nulls
> before doing the same with validTo. Under the current proposal, doing so
> will risk errors, so I will have to check both properties before access
> anyway. Worse, using is_null() or ?: will attempt to retrieve the value, so
> I actually have to be *more* careful, and use isset() or ?? instead.
>

I'm just going to reply to this one line, because this is the root of your
misunderstanding:

The user should never, ever, under any circumstances (*) check for the
initialization of a typed property. The user simply uses the property under
the firm knowledge that it will be initialized -- or there is a bug in the
initialization code. If there is a bug in the initialization code then it
needs to be fixed there, not by introducing a check when working with the
object.

This is very, very different from a nullable property, where it is the
users responsibility to handle the null value. If the user receives a null
value it does *not* indicate an initialization bug and they *must* write
code to account for it.

Please do also remember that uninitialized properties and unset properties
are the same thing, and during the last proposal there was a strong
consensus that we must retain support for property unsetting to be
compatible with Doctrine and other libraries using proxy objects.

Nikita

(*) Okay, there are exceptions, e.g. introspection libraries ;)


>
> Alternatively, I can trust the author of the class, but at that point they
> might as well just use a docblock - the type hint documents their intent,
> but is not enforced.
>
>
> Swift is often cited as a language which gets nullability right, and a lot
> of attention is given to Options, and the compiler ensuring that the None /
> null case is handled; but at least as important is how it handles
> initialisation of non-nullable properties, using "two-phase
> initialisation": https://docs.swift.org/swift-b
> ook/LanguageGuide/Initialization.html
>
> In short, Swift defines a specific point after which all properties must
> have a valid value; before this point, the entire object is invalid for
> read operations, so there is no need for a specific error when accessing
> uninitialised properties.
>
> Swift marks this point by the call to the parent initialiser, ultimately
> going up the chain to the root object. PHP has no root object, and no
> mandatory parent constructor calls, so would need a different way to mark
> this stage. Constructors can already violate the rules the first phase
> should impose, so we can't just use the end of the constructor as the
> validation point.
>
> One possibility would be to add a new keyword like "initialize" which must
> be added to constructors in the presence of non-nullable properties. Above
> that keyword, use of $this other than to write to its properties would be
> an error; afterwards, the constructor could carry on as normal.
>
>
> As I say, this is complex, and it may be best to add nullable typed
> properties first, and give more time to try out approaches to
> initialisation.
>
> Regards,
> --
> Rowan Collins
> [IMSoP]

Reply via email to