Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to customize ternary operators in Swift

Tags:

ios

swift

I know how to customize binary operators, like this

infix operator ** { associativity left precedence 170 }
func ** (left: Double, right: Double) -> Double {
    return pow(left, right)
}

But, how to customize ternary operators in Swift? Can anyone give me some idea? Thanks so much!

like image 373
user2790103 Avatar asked Oct 29 '14 18:10

user2790103


People also ask

What is ternary operator in Swift with example?

1 Ternary Operator in Swift. The ternary conditional operator is used to shorten the code of the if statement. ... 2 Example: Swift Ternary Operator. You pass the exam. ... 3 Ternary operator rather than if…else. The ternary operator can be used to replace specific types of if…else statements. ... 4 Nested Ternary Operators. ...

What is ternary operator in C++?

A ternary operator evaluates a condition and executes a block of code based on the condition. Its syntax is Here, the ternary operator evaluates condition and if condition is true, expression1 is executed. if condition is false, expression2 is executed.

How do you overflow a string in Swift?

Unlike the arithmetic operators in C and Objective-C, the Swift arithmetic operators don’t allow values to overflow by default. You can opt in to value overflow behavior by using Swift’s overflow operators (such as a &+ b ). See Overflow Operators. The addition operator is also supported for String concatenation:

What is the difference between addition and assignment in Swift?

Effectively, the addition and the assignment are combined into one operator that performs both tasks at the same time. The compound assignment operators don’t return a value. For example, you can’t write let b = a += 2. For information about the operators provided by the Swift standard library, see Operator Declarations.


3 Answers

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.

like image 150
Nate Cook Avatar answered Nov 03 '22 00:11

Nate Cook


Here is how you can do it in Swift 4:

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()
    }
}

Usage

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.

like image 22
Noah Wilder Avatar answered Nov 02 '22 23:11

Noah Wilder


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.)

like image 20
rickster Avatar answered Nov 02 '22 22:11

rickster