Extension for operator names

Currently, Swift limits operator names to punctuation-like and mathematical symbols. In particular, you can't use regular identifiers as operator names. While I can understand that there are probably some ambiguity issues with allowing normal identifiers as operator names, that doesn't change the fact that once you leave the realm of well-known operator symbols (e.g. +, -, *, /, etc.) they quickly become complete gibberish. For example, what does /=/ mean in a program, anyway?

While I'm not going to beg for normal identifier support (I suspect that if it were easy or possible, it would have been done already), I'm wondering if a special case could be made for identifiers surrounded by grave accents (back-tics)?

For example, I use the aforementioned /=/ operator as a "reverse ~=" operation. This lets me write:

if x /=/ 1...4 { do something }

(read as "if x is in the range 1...4 then...."

I find this easier to digest than

if 1...4 ~= x { do something }

Perhaps easier to digest, but still clear as mud. It would be really cool to create an operator name that actually has some meaning, such as

if x `in` 1...4 {do something}

Would it be possible to make a special case out of back-tic'd names and allow them as Swift operators?

3 Likes

I like the idea but the use case is not good.

if (1...4).contains(x) { 

That would be such a nice feature! I disagree that the use case is not good as these infix functions are typically used as sugar for DSLs.

However I think the right approach would be to allow regular functions to be infix. So instead of allowing this:

//THIS IS NOT WHAT I RECOMMEND
infix operator in
func in(...) {...}
//or
infix operator `in`
func `in`(...) {...} 
//THIS IS NOT WHAT I RECOMMEND

It should be like below I think:

infix func in(...) {...}

And then it could also be scoped like below:

extension Int {
  infix func in(range: ClosedRange<Int>) {
    range.contains(self)
  }
}

Usage:

let result = 2 in 1...4

Note that unlike infix operator implementation this approach would allow only one parameter (when scoped like above). Such scoping is not allowed for infix operators, only if they’re static, but then they need two arguments

With this approach we don’t have to deal with precedencegroup either. At least looking at Kotlin’s approach they just say that these infix functions “have lower precedence than arithmetic operators, type casts, and the rangeTo operator.” Which seems logical to me, at least after the first glance. See: Functions | Kotlin Documentation

There are some extra details that should be thought through thoroughly.

For example should we allow raw strings for infix functions?

infix func `in range of`(range: ClosedRange<Int>) {
    range.contains(self)
}
let result = 2 `in range of` 1...4

Or could then we allow not just infix but prefix and postfix? Probably not because these look weird:

minus1; `minus`1; minus.1
(1...4)shrunk; (1...4).shrunk

For this specific use case maybe you could just use the math symbol for the membership, but I think infix functions are much better as you don’t have to always find the right symbol, you can just type in the name of the function

infix operator ∈
func ∈(lhs: Int, rhs: ClosedRange<Int>) -> Bool {
    rhs.contains(lhs)
}
let result = 2 ∈ (3...5)