I apologize for the long posts, but Larry asked me to comment on this.

I think the distinction here is that one group is arguing for "state of the

data assertions" while the RFC as implemented is "setter assertion shorthand".

The point of the setter assertions is to provide guarantees about the state of

the data - there is literally no difference.

That is, it doesn't assert that a value IS a given type, but that it can only

be SET TO a given type.

That is literally the same thing - if it can only be set to a given

type, is can only

BE a given type.

The problem here is you want to make exemptions for null as type - as though

nulls aren't types (which they are) and as though nullable types aren't distinct

from the types they're derived from. (again, they are.)

I don't think a complete IS enforcement is possible given PHP's nature.

I don't think that, and I don't expect that - I'm not suggesting we enforce

anything statically, I'm merely suggesting that a constructor needs to satisfy

the constraints specified by the class.

If you've type-hinted a property as int, you don't allow strings - that would be

pointless, right?

But that is precisely the problem we're talking about - if you've type-hinted a

property as non-nullable int, it shouldn't allow null... but that is

literally the

exemption you want to allow for - you just want to annotate the property

with an additional meta-data property "unintialized", which is literally the

same as adding a boolean "is set" for every property in your model.

This isn't a compile time check

either: $b = 'foo';

test($b); It's a runtime TypeError, not compile time.

This actually illustrates my point nicely - the variable $b in this

case has nothing

to do with the state of the parameter var inside the function body,

until you pass

it via a function call. That's perfectly fine. You'll get an error at

the point where

the parameter type-constraint was violated - you can look at a stack-trace and

debug that easily.

Now take your example and call a constructor instead of a function:

class Test {

public string $b;

}

$b = 'foo';

$test = new Test();

Your constructor call generates no run-time error here, and the program

continues to execute with $test in a state that, according to specification of

the class itself, isn't valid.

Since the constructor is how a valid instance of Test gets generated in the

first place, you've missed your only chance to enforce those constraints -

meaning, you've missed the only opportunity you had to verify that the

constructor does in deed generate an instance of Test that lives up to

Test's own specification of what a Test instance is.

To wit, could we add an engine check to scan an object and make sure its

objects are all type valid right-now (viz, nothing is unitialized), and then

call it on selected actions automatically and allow users to call it at

arbitrary times if they are doing more esoteric things?

In my opinion, this is a solution to the problem we created when we decided

every property should internally be annotated with an "initialized" boolean

property - you have all this extra state that wasn't part of the specification

of the class itself, and now you have to deal with that state.

In my opinion, tracking this extra state during the constructor call is

acceptable and necessary in a scripting language like PHP - I never

asked for static type-checking, I'm merely asking for this check to be

built-into the language and performed at run-time when you exit the

constructor, e.g. at the last moment where you can feasibly perform

this check.

If you don't perform that check, you have no guarantees at all.

Consider this example:

function login(int $user_id) {

// ...

}

Inside the body of this function, you can safely assume you have an integer,

right? A basic guarantee provided by the language.

Now consider this alternative:

function login(User $user) {

// ...

}

Inside the body of this function, you'd assume you have a valid instance

of User, right? The same basic guarantee provided by the language.

Wrong. Something might be "unintialized". This might in fact not be a valid

instance of User, it might be only partially initialized.

This is a problem, because I want my type User to be a reliable type - as

reliable as any other type.

Integers for example is nice, because I know I'm going to be able to add,

subtract, multiple, divide, and so on - if you've type-hinted a parameter as

int, you know it has those abilities, you know the + operator isn't going to

fail because there's a special kind of int that doesn't work with the

operator.

We want those same guarantees from our own types, that's all.

In the case where a type doesn't provide such a guarantee, it can explicitly

state that something is nullable - not guaranteed to be set. You have that

option already, you don't need unintialized properties for that.

If there really is a use-case for "optional values that must not

be removed again once they've been set" - and I can't think of one - but

if there is, you can handle that rare case with logic in getters/setters.

The normal everyday use-case is you want to know if a property is going

to be set or not - hence nullable property-types, which are part of the RFC.

Userland work-arounds for basic guarantees that the language should be

providing, in my opinion, are totally unacceptable - in a nutshell, because

basic guarantees provided for other types won't apply to objects.

It's incoherent with the workings of type-hints in the language, and will

make those substantially less useful.