Testing Any for possible Equality

Is there any more elegant way than doing this to test Any for possible eqaulity?

func eq<T>(a: T, b: T) -> Bool? {
    if let a = a as? Double,
        let b = b as? Double {
            return a == b
    }
    else if let a = a as? Int,
        let b = b as? Int {
            return a == b
    }
    else if let a = a as? Range<Int>,
            let b = b as? Range<Int> {
                return a == b
    }
    else if let a = a as? Bool,
        let b = b as? Bool {
            return a == b
    }
// ..........
    else {
        return false
    }
}

let a = reflect(2.0).value
assert(eq(a,a) == true, "should work")


Casting to Equatable doesn't work,

polymorphic functions like following don't work either,

because the dispatch is done at compile time.

func eq<T: Equatable>(a: T, _ b: T) -> Bool? {
     return a == b
}
func eq<T>(a: T, _ b: T) -> Bool? {
     return nil
}

let a = reflect(2.0).value
assert(eq(a,a) == true, "does not work")


What's the reason behind this?

What makes casting to a protocol with generic type requirements at runtime difficult?

Is there a workaround with dynamicType or sth. like this?

Replies

Here's just a variation on your solution:

func eq(a: Any, _ b: Any) -> Bool? {
    switch (a, b) {
    case let (a as Int,     b as Int):    return a == b
    case let (a as Double,  b as Double): return a == b
    case let (a as String,  b as String): return a == b
    // ...
    default: return nil
    }
}


But I'm pretty sure there's no elegant way to test Any for possible equality. And finding ourselves in a situation where we need to do this should perhaps be taken as an indication that our code is "unelegant" at some higher level.


Regarding the reasons behind this, there's this related thread in which Dave Abrahams says:

Code using protocols as types tends to be less optimizable than code using protocols as generic constraints, and for the vast majority of Equatable types there's would be a down-casting burden for programmers (see Protocol Oriented Programming in Swift). When you want heterogeneous comparison among class instances, you can always define a superclass that is Equatable.

The fundamental reason that a protocol with Self requirements can't be used as a type is that the type P could not conform to the protocol P, a situation whose absurdity seems to outweigh its usefulness.