Awesome optional unfolds if conditional

Trying to wrap your head if this is the expected behavior:

When refactoring code that has this form:

let opt:Int? = 9

if let unwrapped = opt {
  if unwrapped > 5 {
    println("Yes")
    // Prints Yes
  }
}

      

I wanted to exclude nested If statements. Using a more compact form like this worked as expected:

if (opt ?? 0) > 5 {
  println("Yes")
  // Prints Yes
}

      

And yet I was surprised that direct comparison to optional also seemed to expand the optional parameter in the conditional expression:

if opt > 5 {
  println("Yes")
  // Prints Yes
}

      

I tested this with other types and they all had the same behavior. Clearly from Apple's documentation, there is debate as to whether any parameter is nil, but I didn't expect it to evaluate with a wrapped value as well.

Am I missing something (to be expected) or is this unsupported option behavior? It seems to be much easier to combine conditionals with options.

Greg

+3


source to share


1 answer


One of the definitions > operator

:

func ><T : _Comparable>(lhs: T?, rhs: T?) -> Bool

      

The compiler seems to use this version of the function to compare Int?

against 5

. We can confirm this by directly using swiftc

and asking it to output the Fast Intermediate Language (SIL):

swiftc -emit-silgen compare.swift

      

It outputs the resulting code, and if we dig it out, we can see that the function calls it to be used for comparison:

// function_ref Swift.> infix <A : Swift._Comparable>(Swift.Optional<A>, Swift.Optional<A>) -> Swift.Bool
%17 = function_ref @_TFSsoi1gUSs11_Comparable__FTGSqQ__GSqQ___Sb : $@thin <τ_0_0 where τ_0_0 : _Comparable> (@in Optional<τ_0_0>, @in Optional<τ_0_0>) -> Bool // user: %28

      

Which shows that it is indeed using a version of the operator >

that takes two Optional

s.

But is 5

not Optional

, so how does it work?



Well, if we look a little deeper into the SIL code, we can see how to do this. Apparently Swift has the ability to do something opposite to optional binding, which is to inject an optional value into Optional

one:

// function_ref Swift._injectValueIntoOptional <A>(A) -> Swift.Optional<A>
%25 = function_ref @_TFSs24_injectValueIntoOptionalU__FQ_GSqQ__ : $@thin <τ_0_0> (@out Optional<τ_0_0>, @in τ_0_0) -> () // user: %26

      

So what appears to be happening is that we are basically doing something similar (but not quite like that):

let opt: Int? = 9
if opt > (5 as Int?) {
    println("Yes")
}

      

Note: This works even if we do something like:

let opt: Int? = 9
let five: Int = 5
if opt > five {
   println("Yes")
}

      

it will add five

to anyway Optional

so that it can do the comparison.

+3


source







All Articles