kelan.io

Type Inferior-ence

I just got tripped up by this unexpected outcome of type inference, and wanted to share it.

Let's say we have this enum that makes sense for our project.
enum Coverage {
    case All
    case Partial
    case None
}
And this function that takes the enum as an argument. But, let's make it Optional so that the caller can omit it to use a default value.
/// - parameter covering: If `nil`, use the default coverage.
func applyPaint(covering coverage: Coverage? = nil) {
    if let coverage = coverage {
        print("covering: \(coverage)")
    }
    else {
        print("got nil; using default coverage")
    }
}
Now, lets use it a bit.
applyPaint(covering: .All)
covering: All
applyPaint(covering: .Partial)
covering: Partial
applyPaint(covering: .None)
got nil; using deafult coverage

Wait, what happened in that last one, with .None?? We passed a non-nil value, but it's acting like it got nil!

Well, remember that an Optional is actually an enum:

enum Optional<Wrapped> {
    case None
    case Some(Wrapped)
}

And that nil is just syntactic sugar for Optional.None. So, the compiler's type inference is assuming that we meant Optional.None, instead of Coverage.None. Facepalm!

Solutions

There are some simple ways to avoid this issue.

We can just pass the full enum name as the argument when calling our function.

But, that means we need to remember to do that in all cases that might call this, which feels very error-prone.

applyPaint(covering: Coverage.None)
covering: None
So instead, we can rename our enum case, to avoid the ambiguity altogether.
enum Coverage {
    case Everything
    case Partial
    case Nothing
}

applyPaint(covering: .Nothing)
covering: Nothing

Notes

I've filed this as SR-2176.

Thanks

As usual, thanks to Jacob for helping me figure out this fun mishap.