How to determine if a generic is an optional in Swift?

You can’t compare an arbitrary value to nil (EDIT: but see Sulthan’s comment below; it may be that we should be able to compare arbitrary values to nil; the rest of this paragraph may be true today, but only due to a compiler bug). While Optional has some bits of syntactic sugar applied to it, it’s really just a enum, and nil is just Optional.None. You want one behavior for one type (Optional) and another behavior for all other types. Swift has that via generics, just not in extensions. You have to turn it around into a function:

func realCount<T>(x: [T?]) -> Int {
  return countElements(filter(x, { $0.getLogicValue() } ) )
}

func realCount<T>(x: [T]) -> Int {
  return countElements(x)
}

let l = [1,2,3]
let lop:[Int?] = [1, nil, 2]

let countL = realCount(l) // 3
let countLop = realCount(lop) // 2

This approach is much more flexible. Optional is just one of many types you would want to flatMap this way (for example, you could use this same technique to handle Result).


EDIT: You can take this further by creating a protocol for things you consider “real.” That way you don’t have to confine this to Optionals. For example:

protocol Realizable {
  func isReal() -> Bool
}

extension Optional: Realizable {
  func isReal() -> Bool { return self.getLogicValue() }
}

func countReal<S:Collection>(x: S) -> S.IndexType.DistanceType {
  return countElements(x)
}

func countReal<S:Collection where S.GeneratorType.Element:Realizable>(x: S) -> Int {
  return countElements(filter(x, {$0.isReal()}))
}

This says, if I pass a collection of “realizable” things, then filter them against their rule. Otherwise, just count them. While I probably wouldn’t really use this function (it seems very special-case), the concept is useful. Later callers can add new “realizable” types without modifying any of your code (or even knowing how they’re implemented). And this shows how to have a default behavior for things that don’t implement your protocol.

BTW, I’m using Collections here just because they’re easier to count (and I’m being a bit sloppy about the return types; notice one is the DistanceType and the other is an Int). Getting the types right on generic Collection-based functions is still kind of tricky (and often crashes the compiler). I suspect this will all improve in the next betas.

Leave a Comment