On Dec 10, 2014, at 9:19 AM, Andrea Faulds <a...@ajf.me> wrote: > Hi again, > > I was in favour of this, but upon further thought, I’m not sure I am. I > remember I also initially liked it in Hack and then lost interest. > > First, how is this substantially different from catching an exception? With > Nikita’s Exceptions in the Engine RFC, something like this would be possible: > > try { > return $foo->bar()->qux()->elePHPant()->dance(); > } catch (EngineException $e) { > return NULL; > } > > That would essentially do the same as what you’re proposing: > > return $foo?->bar()?->qux()?->elePHPant()?->dance(); > > Are there many instances where there’d be a substantial benefit to using ?-> > versus catching an exception?
It's an API decision up to the implementors of the object. If you like using exceptions for *every* failure, then go for it, this syntax won't cost you anything or get in your way :) However, for a lot of failures, I don't feel that exceptions are appropriate. I tend to only use them for exceptional behavior -- usually, some failure that needs to be propagated up a few levels up the stack, to an appropriate error boundary. This doesn't necessarily mean a completely unrecoverable error, but it's *locally* unrecoverable. For things that are completely recoverable, returning null might be more appropriate and more lightweight, and then this nullsafe operator would be useful at that point. Let me talk about a specific example to illustrate the difference, loading the User object in the context of FB news feed. Privacy checks are built in to the User object, so you can't get a User you can't see, but how you want to deal with that failure depends on the callsite. User actually has two functions, one which returns null if you can't see the User, and the other which throws an exception. Suppose you are loading a news feed story, and you want to display the "actor" of the story (the person whose name appears at the top of the story). You'll want to use the "throw exception" variant there -- if you can't see the actor, you can't display the story at all, and so you should throw an exception. An error boundary in the news feed infra code is what catches the exception, several levels up, and just removes that single story from feed, and moves on. It's locally unrecoverable, though globally we can still load feed. In the other direction, if you've got most of the story loaded and you just want the list of Users who have "liked" the post, you probably want to use the returns-null version, and then use the ?-> operator to get the name of that User. It's completely locally recoverable, some User you can't see just gets left off the list of likers, great. We don't want to abort any operation at all, we can keep going; exceptions are not really the appropriate tool to use here. Maybe you disagree with the above, and the use of exceptions -- that's fine, I can totally buy arguments that the above isn't the way you or others want to work. But not everyone does -- I at least think it makes sense to work the way I've described, and there are a lot of points on that spectrum, and this new operator makes it easy to operate at different points there. > Second, not short-circuiting is rather unintuitive. I think most people would > expect it to short-circuit. Note that ?? and isset() short-circuit, so this > would be inconsistent with existing NULL-checking operators. So why, then, > are you proposing that it should not short-circuit? Is there some obscure > case that this makes easier? If anything, I’d expect that short-circuiting is > the more useful behaviour. For example, take the following hypothetical line > of code: > > $elePHPantParty->danceClub?->addToDanceFloor($elePHPantPool->remove()); > > If we have the short-circuiting behaviour, then if $elePHPantParty->danceClub > is NULL, ->addToDanceFloor isn’t called and an ElePHPant isn’t removed from > $elePHPantPool. On the other hand, if ->danceClub isn’t NULL, > ->addToDanceFloor is called and an ElePHPant will be removed from > $elePHPantPool. > > With the non-short-circuiting behaviour, this code would have to be longer, > and you couldn’t use ?-> here. Instead, you’d have to write this: > > if ($elePHPantParty->danceClub !== NULL) { > $elePHPantParty->danceClub->addToDanceFloor($elePHPantPool->remove()); > } > > It is, admittedly, a hypothetical scenario, but it does demonstrate why > having it not short-circuit is less useful. Are there any examples where > short-circuiting is a problem? I think the short-circuiting behavior is the most intuitive, since it looks like a method call, and evaluating arguments is what you do to prepare for a method call -- and this RFC just makes the method call not actually happen at the last moment. That said, although I feel pretty strongly about this, I also didn't find a good example where it mattered either way when I went looking at existing usages in Hack, and your example is indeed an interesting one. Let me spend some more time looking for examples and get back to you. > Third, as has already been mentioned, this doesn’t support ?-> for > properties, which is both unintuitive (people will reasonably expect it to > work) and makes it a lot less useful, because you won’t always be dealing > with method calls. While $foo?->bar()?->foobar() might work, what about > something like $foo?->bar?->foobar(), where one of the links in the chain is > a property? This is not at all an unlikely scenario. If internals thinks this is a feature that would make or break the RFC, I'd be willing to add it. I just figured that something smaller would be easier to propose, implement, etc etc for my first RFC :) > Finally, this may encourage bad code. As someone helpfully pointed out in the > reddit discussion, this encourages breaking the Law of Demeter, i.e. that > objects should only talk to their immediate friends. Usually, long chains of > method calls across multiple objects are not a good idea, yet the main > benefit of this RFC is to reduce the boilerplate needed for that scenario. I agree that long method chains are probably a bad idea, but one or two is fine in a lot of cases. But I very vehemently disagree that method chains are *always* a bad idea (i.e., the Law of Demeter) -- or basically any hard and fast rule about programming, for that matter. It can be done tastefully, and for tasteful uses this reduces a lot of boilerplate. Basically every language feature can be abused, and I don't think this one is particularly more prone to abuse than anything else. Josh -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php