I know how to customize binary operators, like this
infix operator ** { associativity left precedence 170 }
func ** (left: Double, right: Double) -> Doubl
A "true" ternary operator such as _ ? _ : _
requires language support. Swift allows creating and customizing only unary and binary operators.
You can use the technique in @NateCook's answer to make a pair of binary operators which together work like a ternary operator, but they're still independent binary operators -- you can use either on its own. (By contrast, _ ? _ : _
is only a ternary operator; _ ? _
and _ : _
can't be used individually.)
Of course, why stop there? You could chain more binary operators to create quaternary operators, and so on. For extra credit, try making yourself an extended spaceship operator:
let c: String = a <=> b
|<| "a < b"
|=| "a = b"
|>| "a > b"
(...but please do this as an academic exercise only, or anyone else who works with code you write will hate you.)
You can actually do this by declaring two separate operators that work together and using a curried function for one of the operators.
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. That is, 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'll evaluate that part first and return a function:
infix operator +- { precedence 60 }
infix operator +|- { precedence 70 }
Then define the functions - we'll define the second first:
func +|-<T>(lhs: @autoclosure () -> T, rhs: @autoclosure () -> T)(left: Bool) -> T {
return left ? lhs() : rhs()
}
The important part here is that this function is curried -- if you only call it with the first two parameters, instead of returning a T
value, it returns a (left: Bool) -> T
function. That becomes the second parameter of the function for our first operator:
func +-<I: SignedIntegerType, T>(lhs: I, rhs: (left: Bool) -> T) -> T {
return rhs(left: lhs >= 0)
}
And now we can use our "ternary" operator, like this:
for i in -1...1 {
let sign = i +- "" +|- "-"
println("\(i): '\(sign)'")
}
// -1: '-'
// 0: ''
// 1: ''
Note: I wrote a blog post on this subject with another example.
precedencegroup SecondaryTernaryPrecedence {
associativity: right
higherThan: TernaryPrecedence
lowerThan: LogicalDisjunctionPrecedence
}
infix operator ~ : SecondaryTernaryPrecedence
func ~ <T>(lhs: @autoclosure () -> Bool, rhs: @escaping @autoclosure () -> T) -> (Bool, () -> T) {
return (lhs(), rhs)
}
infix operator >< : TernaryPrecedence
@discardableResult func >< <T>(lhs: (Bool, () -> T), rhs: @escaping @autoclosure () -> T) -> T {
if lhs.0 {
return lhs.1()
} else {
return rhs()
}
}
let n = false ~ "it was true" >< "it was false" //"it was false"
true ~ print("it was true") >< print("it was false")
// Prints "it was true"
Note: While this is not a true ternary operator per se, as it uses two infix operators in conjunction with one another, it does in fact somewhat emulate its behaviour when the two operators are used together in the fashion presented above.