Allow an underscore (_) on the right side of the Ternary (?: ) and the Nil-Coalescing (? ?) operators

It would reduce some common if clutter without making the code harder to understand, or conflicting with unary operators.

Examples:

  • something = optionalThing ?? _

  • optionaThing != nil ? doSomething() : _

The above seems cleaner than what we currently have to write:

something = optionalThing ?? something
This is specially weird, because if you wrote something = something it would be a compile time error! "Assigning a variable to itself", but that is what this expression would resolve to if optionalThing == nil yet it's allowed and it works.

optionaThing != nil ? doSomething() : ()

or:

if let optionalThing = optionalThing {
    something = optionalThing
}

or:

if optionalThing != nil {
    something = optionalThing!
}

Especially if you have to write many such lines as these.

Letting us be able to say "else do nothing" in the ?: and ?? operators, instead of always specifying a superficial fallback operation, would be no different than not forcing every if to have an else clause, and the _ underscore pattern is already used with for in and other places in the language.

I am not so sure that the terseness aids in readability.

To replace x = y ?? x, I'd like to see an assignment operator: ?=, in the same vein as +=, which assigns only if the RHS is not nil.

3 Likes

Just 3 lines to implement:

infix operator ?=
func ?=<T>(lhs: inout T, rhs: T?) {
    lhs = rhs ?? lhs
}

var some: Int = 0
var otherA: Int?
var otherB: Int = 5

some ?= otherA // 0
some ?= otherB // 5

;-)

1 Like

It‘s a safe one liner since the assignment operator returns Void and the function discards the result which is Void. otherA.map { some = $0 }

I did exactly the same thing after writing my earlier comment :slight_smile:

1 Like

... where is has a different meaning. The "assign if nil" operator imho is not only easier to implement, but also to understand.

2 Likes

I feel like ?= has the wrong connotation, because putting the ? next to a variable usually means optional chaining, etc. So this implies to me that the left hand side is optional and the assignment is only done if the left hand side is non-nil, which is an opposite meaning entirely. I think =? or similar might make more sense if you want to define a custom operator.

1 Like

I thought of that, too, after my comment, and after implementing it myself. I don't think that either connotation is obviously-correct for either spelling (?= or =?), and thus is best kept out of the standard library.

Maybe something like

x = _ ?? y

could be allowed for cases where x is an optional var, which would be conditionally replaced with y. However, this would be a new use for _, and is probably not a good idea.

In that case I think the obvious solution is to spell the operator ??=

3 Likes

Creating new operators is less intuitive than simply adopting intuitive behavior for the existing operators.

The ?: and ?? operators are shorthand for an if statement, after all. They read as "If that, then do this, else do this."

However, they currently do not offer an intuitive way to write "If that, then do this, else do nothing."

Right now, we have to resort to "hacks" if we want to say "else do nothing", like writing x = y ?? x or a == b ? f() : ()

So, saying "else do nothing" in those operators is not disallowed, but there is no elegant way to write it.

It's not really “else do nothing”, it's “else magically return the value of the variable on the left hand side of the assignment”. This is not very intuitive to me, because if I use y ?? _ in any other context it presumably won't work, making it the only operator with this strange contextual behaviour that doesn't seem to generalise well (i.e. How is this implemented? Can I write my own operators that behave this way? If so, how?). It also doesn't align that well with the use of the _ elsewhere, which is more “don't care” or “discard” than “do nothing”.

??= probably makes the most sense, but it was already proposed and rejected before, so a strong proposal would be required.

2 Likes

My take is that the ternary operators (?: and ??) are not short-hand if statements. They are special functions that return one of two values based on a conditional. This is why they are operators and not statements. I would look at single-argument versions as trying to write x = max(y, _).

1 Like

The nice thing about Swift is that operators only benefit from being in a standard library if there's a risk that two or more dependancies will each define that operator. As the rational of the previous proposal points out, this operator is rather niche in Swift since it doesn't remove the optionality of a variable, therefore it seems like a good fit to put in one's personal utility library.

I understand now, especially the point about them being special functions.

Allowing an underscore would break things like calling doSomething(with nonNilParameter: Int) with doSomething(with: a ?? _) or doSomething(with: a == b ? x : _) and limiting such usage of an _ in those operators to assignments only would also increase inconsistency.

There is still the possibility of issuing a compile-time error on such usage as the above example, but I suppose then we would be getting into an increasing amount of exceptions to what should be a simple rule.

Maybe this propsoal could be added to the list of Commonly Rejected Changes?


Having conceded that, I still feel that there should be something done to reduce the boilerplate in common tasks like:

if let optionalThing = optionalThing {
    something = optionalThing
}

but custom operators don't seem like the right answer; they would make such common tasks appear a little different in every codebase that uses different custom operators, for one.