Why are operators not available as static type members?

I was always wondering why this still not available in Swift.

Int.+(lhs:rhs:)
Int.+=
Int.==
// etc.

In some function that expect a function with the signature (T, T) -> T we can easily pass an infix operator function + if T is known to be a type with such an operator.

func foo(_: (Int, Int) -> Int) {}
func bar<T>(_: (T, T) -> T) {}

bar((+) as (Int, Int) -> Int) // okay

foo(+)     // okay
foo(Int.+) // Use of unresolved operator '.+'

Custom operators that contain a dot such as .+ could be referenced as Int..+ or if that's ambiguous then as Int.(.+).

Is there a limitation that cannot be lifted, or any other issue that prevents us from accessing these static functions from a concrete type?

3 Likes

Parsing is pretty much the only reason I know of. I think we'd have to always parenthesize it in case someone defines .+ as a postfix operator that applies to types (even though you're supposed to write that Int.self.+), but accepting the parenthesized form seems like it'd be a fairly non-controversial evolution proposal, and it's probably not too hard for someone to implement.

Another possible spelling: Int.`+` and Int.`.+`. I might like this one even better because it doesn't even really need any new rules; it just says it's okay to use backticks with operators in member position, and then you've got an escaped identifier like anywhere else.

16 Likes

I had not noticed yet that this was missing, but sooner or later I’d have tried it and been surprised that it didn’t work. I agree it would be good.

I wonder if anything can be done about the static/global (in)difference?

extension Int {
    static func ≤(lhs: Int, rhs: Int) { ... }
}
func ≥(lhs: Int, rhs: Int) { ... }

Until now, they’ve both been usable in all the same ways, so would it confuse clients that Int.≤ compiles but Int.≥ doesn’t? What if they don’t know where in the source to look for the declaration? Xcode doesn’t show Quick Help or Jump to Definition for operators, and even if it did, something that doesn’t compile cannot be looked up.

2 Likes

My 2c: backticks are better than parentheses because everyone is already familiar with them as a way to "escape" problematic tokens. I doubt anyone who tried to write Int.+ would reach for parentheses as a solution before trying backticks.

5 Likes

Not sure if this is relevant but one thing I was wondering today

Lets say if I want to see
if 'T' is equal to 'A'

For example it would be nice to be able to write a function is under a Equatable extension that does this. Idk what the parameter type would be for this operator if I were to write this function.

let T : Int = 5
let A : Int = 5 
if T.is(==, to: A) {
    print("yes")
} else {
    print("no")
}

The statement T.is(==, to: A) looks like a worthy sugar addition to me thats very neat

This is completely off-topic here. Furthermore I would be against such a method because it hurts readability from my point of view. If you like it, you can easily define it yourself in your own code base:

extension Equatable {
  func ˋisˋ(
    _ condition: (Self, Self) -> Bool,
    to other: Self
  ) -> Bool {
    return condition(self, other)
  }
}

I personally don‘t think it‘s worth considering of it‘s addition to the stdlib compared to methods like ˋisEqual(to:)ˋ or ˋisNotEqual(to:)ˋ but this is again off-topic to this thread.

2 Likes