Stas, This is pretty bad by itself. But the fact that everybody would be using > this feature in this way - since they would think "I don't need null > default, null is always the default and I'd initialize it in the ctor > anyway", not realizing that declaring "public DateTime $foo" blocks > unset - is even worse. At least with setter you need to explicitly > define typehinted setter - and there you have a place to think about it. > With "public DateTime $foo" 99% of people would never even remember to > think about it - until they'd start getting mysterious fatal errors on > unsets. That's why I think it makes it worse.
So your argument is that people who don't take the time to understand how something works will somehow be worse off then they would be today? They can currently write all sorts of bastardized code that raises fatal errors. Should we try to stop them doing that as well? Should we implement BASIC style syntax where each line that generates an error just goes to the next line like nothing even happend (oh, right, we already do that, we should be ashamed of ourselves). Show me a public (real life) code base that calls unset on a parameter that's type hinted by a setter. Where you have either __set or a setBar() setter, and then somewhere else in the class you call unset() on the property. While I do understand that it's a possibility, I can't help but feel that you're completely grasping at straws here... > > 1. First of all, there never is a guarantee for everything. All kinds of > > This is a useless platitude. I've outlines specific scenarios where it > fails to do what it seemingly promises to do. Yes, and all those scenarios are complete far edge-cases. Not one that I've seen yet is something that I've sen in production code more than once in my career... > > 2. Secondly, the main purpose of typehints is to prevent people setting > > incorrect values. If you know that only a "DateTime" instance is valid > > and that NULL is not valid, then you should allow only that, only the > > DateTime. Allowing NULL absolutely doesn't make sense (as it's not a > > Allowing NULL is *inevitable*, that's the whole point, since that's what > the value is when the object is created, and you can not guarantee this > value was initialized. The only thing you can do is to *pretend* it can > not be null, even though you know it can be, and ignore the cases where > it is null, because you're sure your code is correct. Again, before initialization (__construct is completed) all bets are off. You can screw yourself up as much as you want. But if I provide a final constructor, after initialization, the property should never be null unless I explicitly make it so INSIDE MY CLASS. If you, outside my class, want to screw with that, I want you to get a fatal error. That's the entire point of defensive programming. I can do what I'd like, but I don't want you to screw something up in a way that I don't expect. And that's what this RFC is talking about adding... > > Your argumentation goes along the lines of "It's possible that this > > property could maybe end up being NULL, so it does not make sense to > > prevent people from assigning NULL". The second part of that sentence > > You can easily prevent people from assigning NULL, what you can not do > is guarantee the property never returns NULL (at least not with the > short syntax, the explicit accessor is a different thing, there you can > do it just fine). That's what is wrong with this thing. This shortcut > doesn't actually do what people would think it does. It is misleading. Yet again, post construct, if I can guarantee that nobody outside my class can assign null, I can guarantee that the property will never return null. Simple as that. It reduces the exposure area to the class itself. > > 3. The constructor is typically (or at least that's how it *should be*) > > small and doesn't do work, so there isn't much to do wrong. But sure, it > > "Small" and "work" have wildly different definitions. In real life, > constructors frequently call out to other methods when initializing. > E.g. somehing like: > public function __construct() { $this->log = Logger::getInstance(); > $this->db = DBFactory::getInstance(); } > or something like that, happens all the time. Now imagine Logger somehow > tries to use something that uses $this->db and assumes it's not null - > since it can never be null, we declared it not null! Funny you bring that up. Because it's literally not possible in the code that you wrote to throw the error case you describe. That's because in the constructor there's literally no other reference to the active class other than $this. So the only possible way for that to happen would be to explicitly pass $this to the logger call. Otherwise, as the code you wrote would always, 100% of the time, work... And that's the point here. Not that it's not *possible* to break the system, but that they are such extreme edge-cases that throwing an error isn't a big deal. And they are such extreme edge-cases that throwing away an entire concept is down right irresponsible (it'd be like throwing away OOP as a concept because an object might not quack like a duck if you choose to use duck-typing)... > > I'd summarize this as follows: The typehint prevents all > > incorrectly-typed assignments, but it does not prevent missing > > initialization. Your argumentation is that because it can't prevent > > missing initialization, we shouldn't bother with preventing incorrect > > assignments either. I hope you realize that this makes no sense. > > It's not my argument. My argument is this syntax makes promise of the > value never be null and it is specifically proposed with this sole > purpose (and argument is unless it is the promise this syntax has no > value at all). And it does not do that. In addition, it breaks unset() > which would be completely unexpected by 99% of users. I think in this > way it will do more harm than good as it would lead people to write code > which behaves worse than if this syntax did not exist. > No. The syntax promises that the value will never be *assigned* a value of null. That's very different from "never will be null". One I can control (by assigning it a value, I know it can never be null again), and the other I cannot (with initialization race-conditions as you specify)... Let's not handicap the language because there may be some code that someone might write in the next 20 years that might cause something that results in a fatal error that wouldn't be obvious. Except that if that person understood the documented language, it would be obvious. So it's not really possible (running into an unexpected error) unless that person also didn't read the documentation.. So if we're now designing language features that need to behave 100% intuitively for every single programmer without the requirement of reading documentation, let's stop making all changes to the language. Because *all* changes are going to be unintuitive to *someone*... Let's be realistic here... Anthony