@mayoff Thank you. Each thread mentions that this has been discussed in the past, sometimes linking to the more senior thread, sometimes letting the trail go cold... I'd love to know the actual first time this was posted about. Perhaps it's lost history.
I of course have not read all of the discussion on all of the threads, but @xwu's comment here jumped out to me as an understandable grievance. Here's my thinking on this:
We have a couple situations in which we are already accustomed to having to reason about possibly skipped code:
optionalValue?.functionWithSideEffects() // May not be run
and
guard let value = optionalValue else { return }
functionWithSideEffects(input: value) // May not be run
for example. In both of these cases there is a simple reason that code is skipped - it's because it can't be run. It is dependent on a state which did not come to pass. But arguments of functions are siblings in their space - by definition they aren't dependent on one another since they are in parallel. Therefore, if one happens to fail there's no reason whatsoever that the rest of them can't run, and therefore they should. Yes, when typed out, the horizontal layout of function arguments combined with ?
makes them somewhat similar in appearance to an optional chain, in which things to the right are skipped if things to the left are nil. But fundamentally they are not at all the same and there is no actual reason that anyone should expect function arguments to the right to be skipped due to a failure in a function argument to the left. My vote would be that all arguments are first evaluated, then if one of the unwrapped arguments is nil the function evaluation is skipped.
Although it is true and occasionally relevant that function arguments are evaluated left to right, it is more common that arguments passed to functions have no side-effects and therefore that the order of evaluation is irrelevant. Passing the result of a side-effect function to another function is a very dubious practice that I don't think should be especially catered to, merely tolerated. If the decided behavior, as is my vote, is evaluation of every parameter regardless of failures, one can still of course abort due to the failure of a specific argument with a separate line of code:
guard let mustNotBeBil = mightBeNil1 else { return }
doSomething(a: mustNotBeNil, b: mightBeNil2?)