I apologize for the long posts, but Larry asked me to comment on this. On Thu, Sep 20, 2018 at 5:52 PM Larry Garfield <la...@garfieldtech.com> wrote:
> 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. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php