I don’t think it’s really an inconsistency. The question mark before accessing a property / calling a function implies that the ensuing access is optional, as in “ negate x (optionally)”. On the other hand, using such syntax with operators doesn’t seem that intuitive. The question-mark sugar could already be considered too confusing for beginners. Thus, visually separating the question mark for optional chaining (with a space character) from the declaration to be chained, comes with readability problems.
Not to say that this is "the reason" why it doesn't work, but it would be pretty weird to have a? + b but not a + b?, since arithmetic operators generally treat both operands equally. If we were going to do something like this, I would probably prefer a spelling like a +? b or a ?+ b that could chain on either or both sides.
We already have that situation with unary operators: as described in the thread Optional chaining on prefix operators, optional chaining works with postfix operators x?... but not prefix operators ...x?.
I don’t think it’s surprising to say, “optional chaining takes an optional on the left and a function on the right”.
• • •
My actual use-case for this, besides a general sense that it ought to work for consistency, is dynamic casting:
But if the dynamic cast is to Optional (or ExpressibleByNilLiteral), and the right-hand side is nil, then it compares at the top-level instead of at the wrapped-value level.
And of course, one might want a different operator besides ==. Maybe <, maybe +, maybe forming a range, or something else entirely. Optional promotion won’t let those work.
While I see the apparent inconsistency at the call site, there is a deeper difference.
x + 3 is not a syntactic variant of x.+(3) — for some hypothetical instance method named +(_:) — but is rather +(x, 3) — for some actual global/static function whose name is +(_:_:).
So, I see the inconsistency as a reflection of the fact that optional chaining doesn't reach "into" function parameters, though that feature has been requested before , e.g. here and here.
The other difference is that there's no clear chaining in x? + 3, which is another way of saying what @scanon said. With a form like x?.plus(3) it's obvious where the "chain points" are in an expression. With x? + 3, it's not entirely clear which subexpressions are evaluated before or after the point where an overall nil result can be produced.
Assuming all of that could be sorted out syntactically and semantically, I think operator chaining would still be problematic, because it overloads ? use-cases so far as to potentially make ? seem like a magical DWIM operator.
It's not a terrible idea, I think, but I'd sure prefer if we could continue to manage without it.
…so it turns out that users can already make this work, by defining new precedence groups that are copies of the existing ones but with assignment: true, and redeclaring the operators for that precedence.
let x: Int? = 3
let y = x? + 4
print(y) // Optional(7)
• • •
Does the assignment field of a precedence group do anything other than enable optional chaining for operators?
If not, then making this work seems fairly simple. We could do away with the assignment field entirely, and make all operators work with optional chaining. Or at least, we could make the default be to allow optional chaining.
The assignment of a precedence group specifies the precedence of an operator when used in an operation that includes optional chaining. When set to true , an operator in the corresponding precedence group uses the same grouping rules during optional chaining as the assignment operators from the standard library. Otherwise, when set to false or omitted, operators in the precedence group follows the same optional chaining rules as operators that don’t perform assignment.
Yes, and I wrote a reply to you a year ago with the answer. Namely, try hoisting cannot be expressed in terms of Swift precedence but looks to whether the neighboring operator is an assignment operator.
Even if we didn't have any special rules for assignment operators, you still couldn't just allow optional chaining for non-assignment operators and expect things to go well. Recall that a chain implies that there are multiple "links":
In other words, using parens to indicate order of operations, we have instead (notionally) foo?(.bar == nil). This is a totally different expression than the status quo shown above; users (or, at least, any user who's written any code already) actually wouldn't want this chaining for non-assignment operations.