Search code examples
swiftoption-typeoptional-chaining

Why can't I call map<U>(_ transform: (Wrapped) -> U) -> U? and use optional chaining at the same time?


I know that optional chaining like this:

someOptional?.someProperty

is basically

someOptional.map { $0.someProperty }

However, I found that doing both at the same time is not possible:

// someOptional?.someProperty evaluates to an optional type, right?
// so the map method should exist!
someOptional?.someProperty.map(someClosure) // can't find "map"

Here's an MCVE:

let s: String? = "Hello"
s?.hashValue.map(Double.init)

I think writing something like the above is more readable than:

s.map { Double($0.hashValue) }

So I would really like a way to use optional chaining and map at the same time.

How do I do this?


Solution

  • Apparently, for some reason, wrapping the first part in brackets worked:

    (s?.hashValue).map(Double.init)
    

    I think this is somehow due to the ? having a lower precedence, or that optional chaining doesn't work the same way as other expressions. Adding the brackets might have caused s?.hashValue to become an expression.

    Edit:

    Here is the relevant excerpt from the Swift reference:

    If a postfix expression that contains an optional-chaining expression is nested inside other postfix expressions, only the outermost expression returns an optional type. In the example below, when c is not nil, its value is unwrapped and used to evaluate .property, the value of which is used to evaluate .performAction(). The entire expression c?.property.performAction() has a value of an optional type.

    So s?.hashValue doesn't actually evaluate to an optional type, because it is nested inside another expression. By adding (), I separated it into another expression, manually making it evaluate to an optional type.