Value returning assignment operator

(Trevör Anne Denise) #1

When using lower level functions it often makes sense to do a while loop as long as a function return value is within a certain range, and to use the return value within the loop. In C we would often see code like this:

while ((foo = bar()) > 0) {

In Swift the = operator doesn't return anything, which is great in general as it avoids mistaking = and ==, but for the cases where you want to use the aforementioned coding style, you can't actually do it without being redundant: I don't see any other way than to do:

var foo = bar()
while (foo > 0) {
    foo = bar()

Is there another existing coding pattern that would be better in this case? My example isn't too complicated so the repetition isn't that ugly, but in cases where the function call has many arguments the redundancy pops out a lot IMO.

If not, why wouldn't we introduce a new operator that would have the same behaviour as the = of C?

Thank you

(Lukas Stabe 🙃) #2

If bar would return an Optional, you could just use while-let, like this:

while let foo = bar(), foo > 0 { ... }

Since bar doesn't seem to return an optional you can make things work by sticking a case in there, which will do the trick (although that is as undiscoverable as it gets):

while case let foo = bar(), foo > 0 { ... }

You might also consider wrapping bar so that it only returns valid values:

func validBar() -> Int? { 
    let b = bar()
    return b > 0 ? b : nil

while let foo = validBar() { ... }

(Trevör Anne Denise) #3

Wow thank you! I'm actually really surprised by the use of while case here, I didn't know that you could use it for an assignment where the right hand side doesn't return an optional, this is great!

(Brent Royal-Gordon) #4

The case solution is surprisingly nice, but if you want a specific syntax just for this, you can also invent a new operator to use with if let or while let:

infix operator >? : ComparisonPrecedence

extension Comparable {
    static func >? (lhs: Self, rhs: Self) -> Self? {
        guard lhs == rhs else { return nil }
        return lhs

while let foo = bar() >? 0 { ... }

(Trevör Anne Denise) #5

That's indeed a cool way to map a value and condition to an optional!
I'm worried about naming this with ">?" which makes me think to a less than operator... on the other hand if we wanted to build such an operator for every existing comparaison operator, for instance suffixing them with a "?", we would end up with oddities such as "!=?" which doesn't look really nice.
I'm also quite curious about the performance impact.

(Trevör Anne Denise) #6

Actually what I was wondering is wether or not it was explicitly decided, at some point, that the if case, while case options where the actual replacement of the C-like = operator? Or is this something that might still evolve?
I mean that it is a nice feature, but definitely not as concise as if there was a dedicated operator, for instance := or <-.

(Frank Swarbrick) #7

For what it's worth...

func <- <T> (lhs: inout T, rhs: T) -> T {
    lhs = rhs
    return lhs

infix operator <- : AssignmentPrecedence

var foo = 0
while (foo <- bar()) > 0 {

One thing I don't like about this is the requirement to set the var to a value first. If we had out to go along with inout...

I'd also prefer := to <-, but the colons are not allowed as part of an operator token. :frowning: