What does epression like optionalVariableName? = <some> mean?

Sorry. Silly question. I did a typo in my code and found some behaviour that is unexpected for me. Of course it is subjective but I could not find anything describing that behaviour in the documentation neither I found the reason why it exists.

var string: String?

string? = "test"

print("\(string)") // prints nil

So the value of the variable says nil. But I dont understand why does it compile at the first place. Shouldn't it be a compiler error or there is a bigger meaning behind?

2 Likes

So you want to say that it is a some kind of syntax sugar meaning: if string != nil { string = "test" }

2 Likes

I am surprised that this non obvious, hardly useful and rarely used feature survived foo so long.

It just falls out from the more useful ability to say foo?.property = x, and we'd have to specifically ban it. I'd be inclined to do that, but to my surprise, people keep saying it's actually useful.

6 Likes

I spent quite some time debugging before I noticed that question mark :)

1 Like

What? Why would you have a mutating forEach on Optional?

Firstly, forEach implies some sort of collection, which Optional is not. And although it clould be considered a kind of zero-or-one collection, it still makes no sense to call it forEach since there are never more than one "element" in this "collection". And if this non-obvious interpretation of Optional is useful, it would probably make more sense to actually conform Optional to Collection rather than overloading concepts haphazardly.

Secondly, even if we do consider it a Collection, we don't have mutating forEach on other collections either, so why consider it "missing"?

If it is useful to have some kind of unwrap-mutate-rewrap operation, we should probably support:

if let inout value = optional {
  value = ...
}

And if we would want a function version of that, maybe a new name is in order.

Yeah, forEach is definitely not the right name for it's mutating counterpart. I use updateEach whenever I need this function on MutableCollections.

While the collection interpretation of Optional is valid, and some other languages have used it, I think I remember one of the core team members post that they had actively decided not to use that interpretation for Swift. Which leads me to believe that the proper name for this function is just, update.

P.S. I want inout bindings almost as much as I want BitwiseCopyable.
P.P.S. update might also be the correct name for the collection version, but that depends on whether it's better to match map/reduce's naming scheme or forEach's.

This is just optional chaining at work. Isn't it?

if let _ = string {
   string = "test"
}

Yes, I find this ability very useful. :slight_smile:

For example, contrast these:

...
let report : ((Error) -> Void)?
... 

if let report {
   report (some_error)
}
...
let report : ((Error) -> Void)?
... 

report? (some_error)

To me these two use cases are very different, and while the second is super useful I can't remember I even needed to reach out for the first:

    string? = "test"
    report?(some_error)

Was it even "free" to achieve? For example this compiles:

var x: Int?
x? += 1

and this doesn't compile:

infix operator **=
func **= <T> (x: inout T, y: T) {/*...*/}
var x: Int?
x? **= 1 // πŸ›‘ Error: Left side of mutating operator has immutable type 'Int?'

In order to achieve the same behaviour with my custom operator I'd have to do something in addition. And apparently this additional work was done to give us this feature in the first place.

1 Like

Yes.

You have to give your operator an appropriate precedence group with assignment: trueβ€”for example, AssignmentPrecedence, just like += has.

This is required for any optional chaining to behave like it does with standard library operators; as @John_McCall says, the case of x? = … falls out for free.

4 Likes

On the surface they look different, but they would be the same if you read them like this:

Operator OptionalLHS RHS

Operator : AssignmentOperator or FuncCallOperator

Yep.

There's also some intimate relationship between operators and optionals:

var x = 0
x += 1          // βœ…
(+=)(&(x), 1)   // βœ…

var y: Int? = 0
y! += 1         // βœ…
(+=)(&(y!), 1)  // βœ…

y? += 1         // βœ…
(+=)(&(y?), 1)  // πŸ›‘ Cannot pass immutable value of type 'Int?' as inout argument
1 Like