Memoji of Jacob giving a thumbs up

Mirroring Kotlin's Elvis Operator in Swift

In many languages, there are ways to coalesce nil values. In Swift, for example, the Nil Coalescing operator is ?? We use this to provide a fallback value when a variable could be nil, like so:

var y: Int? = nil
var x = y ?? 0

In the Kotlin language, there is a similar operator called the "Elvis Operator" because, seriously... it looks a bit like a tiny sideways elvis ?: It's used like:

x = y ?: 0

So why try and mirror it?

Unlike Swift's ??, Kotlin's ?: does more than coalesce nil values, it can throw too!

x = y ?: throw SomeException()

When used this way, x will still be a non-optional type, but the function will stop there if y is null.

To do this in Swift, we would generally need to use a guard statement.

guard let x = y else { 
    throw SomeError() 
}

It's not terrible, but Kotlin's ?: is much nicer, in my opinion.


Writing a Custom Operator in Swift

Swift allows us to write custom operators to handle things like this. Unfortunately, the : character is unavailable for use. So we can't make our operator syntactically identical to Kotlin's.

The operator we'll be creating is an infix operator. Meaning that the operator will fall inbetween its arguments, just like argument1 ?? argument2. In Swift, infix operators must adhere to a Precedence Group. This grouping tells the compiler what order functions should get executed, much like the Order of Operations in mathematics.

There is a lot there, so we won't go into the specifics on all of that in this tutorial.


Unwrap or Throw


Because we can't use Kotlin's ?: or Swift's ?? we'll do something a little different.

infix operator ??? : TernaryPrecedence
func ???<T>(_ left: Optional<T>, right: Error) throws -> T {
    guard let value = left else { throw right }
    return value
}

Using generics, we can pass any optional type to the left hand side of ??? for evaluation! Let's take a look at how it's used.

x = try y ??? SomeError()

It works splendidly! Because of the way Swift handles throwing functions, we do this differently than in Kotlin. try on the left instead of throw on the right.

Unlike Kotlin's Elvis Operator, this does not handle both coalescing a nil value and throwing errors. We'll have 2, ?? and ???, and we'll need to use the them appropriately.

I hope you found this useful!

Tags: