How to check/predicate function type in Swift?

2019-05-12 00:09发布

问题:

For example:

func f(x: Int) -> Int {
    return x
}


func h(f: @escaping (Int) -> Any) {
    if (f is (Int) -> Int) {
        print(f(1))
    } else {
        print("invalid")
    }
}


h(f: f)

I expect it to print out 1 but it actually prints out invalid.

回答1:

There's a workaround using generics:

func intF(x: Int) -> Int {
    return x
}

func stringF(x: Int) -> String {
    return "\(x)"
}

func h<T>(f: (Int) -> T) {
    if (T.self == Int.self) {
        print(f(1))
    } else {
        print("invalid")
    }
}

h(f: intF)    // prints: 1
h(f: stringF) // prints: invalid


回答2:

Using Any is almost always a sign of code smell, you should try to rely as much as possible of the type safety that Swift provides. You can achieve this by making h generic, thus verifiable at compile time.

// the overload that does the actual stuff
func h(f: @escaping (Int) -> Int) {
    print(f(1))
}

// this maps to all other types
func h<T>(f: @escaping (Int) -> T) {
    print("invalid")
}

h { _ in return "15" }  // Invalid
h {  2 * $0 }           // 2

Heck, you could even give up the generic overload, thus you'll have for free compile checks instead of runtime failures (much, much reliable and predictive)



回答3:

You can rewrite h into a generic function:

func h<T>(f: @escaping (Int) -> T) {        
    if T.self == Int.self  {
        print(f(1))
    } else {
        print("invalid")
    }
}

But the better way to to write type-specific overloads for h and a generic catch-all for the rest (if you need it at all).