I ran across an interesting question on Stack Overflow today about how to implement custom ternary operators in Swift. Swift, like most languages, only has a single ternary operator built-in – the conditional ternary operator ?::

let result = booleanValue ? "true" : "false"

Even though Swift only supports unary and binary operators for operator overloading, it’s possible to implement our own ternary operators by declaring two separate operators that work together and using a curried function for one of the operators.

What’s your sign?

For our first example, let’s declare a ternary operator x +- y +|- z that will check the sign of the initial value x, and then return the second value y if the sign is zero or positive and the final value z if the sign is negative. This is similar to the existing ?: operator – we should be able to write:

let sign = -5 +- "non-negative" +|- "negative"
// sign is now "negative"

We’ll start by declaring the two operators. The important part is to have higher precedence on the second operator - we want to evaluate that part first.

infix operator +- { precedence 60 }
infix operator +|- { precedence 70 }

Now we define the functions - we’ll begin by defining the second operator (+|-) as a curried function that takes two autoclosures and a Bool. There’s a lot going on there – first, a couple definitions:

Autoclosures

An autoclosure parameter is a way of wrapping an expression in a closure so that it doesn’t immediately get executed. We want to make sure we only evaluate one side of the second operator after we’ve looked at the sign of the initial value. Using an autoclosure means that instead of receiving two values of type T in our function, we receive two closures that, when called, return a value of type T.

Curried functions

A curried function is one that, instead of being called one time with multiple parameters, can instead be called multiple times, adding a parameter each time. The first time a curried function is called, it returns another function that can take the remaining arguments, which can be called in the same way until no arguments remain. Apple gives an example of using a curried function two add integers.

Back to our second operator - the first time we call it will be with the two sides of that operator, and we’ll call the result of that with a Bool to select which autoclosure we’ll evaluate:1

func +|-<T>(lhs: @autoclosure () -> T, rhs: @autoclosure () -> T)(left: Bool) -> T {
    return left ? lhs() : rhs()
}

The remaining part of that function becomes the second parameter of the function for our first operator, and we can use our new “ternary” operator.

func +-<I: SignedIntegerType, T>(lhs: I, rhs: (left: Bool) -> T) -> T {
    return rhs(left: lhs >= 0)
}

println( -5 +- "non-negative" +|- "negative" )
> > negative

Here’s how it gets executed:

  1. Due to the precedence of +|- being higher than +-, this subexpression is evaluated first: "non-negative" +|- "negative"
  2. Each of the two sides of that subexpression (each of the two strings) is wrapped in a closure, waiting to be called
  3. Those two closures are returned from the +|- function captured inside another function which only takes a Bool
  4. That new function is passed as the right-hand side to the +- operator function, which evaluates the sign of lhs and passes it into that new function.
  5. Lastly, the boolean value determines which of the autoclosures created in step 2 gets executed - the result of the chosen closure is the result of the whole expression.

Modular exponentiation

This same technique can be used for other ternary operations, like calculating modular exponentiation. Modular exponentiation calculates the modulus m of a number b raised to the e power – a ternary operation. Here are the declarations, following the same structure:

// declare operators
infix operator *%* { precedence 50 }
infix operator %*% { precedence 60 }

// second operator first - no need for autoclosures here
func %*%(e: Int, m: Int)(b: Int) -> Int {
    var c = 1
    for _ in 0..<e {
        c = (b * c) % m
    }
    return c
}

// here we simply call the new function with the lhs
func *%*(lhs: Int, rhs: (b: Int) -> Int) -> Int {
    return rhs(b: lhs)
}

And now we have a ternary modpow operator:

let modexp = 4 *%* 13 %*% 497
println(modexp)
> > 445 

Thanks for reading!

  1. Bonus points for using the ternary operator when declaring a ternary operator?