Search code examples
swiftfunctionassignment-operator

How do I reassign a function which takes a generic type argument in Swift?


Disclosure, this is my first day writing any Swift and I'm coming from a JS/TS background.

I'm used to simply reassigning functions, like the following:

let assertEqual = XCTAssertEqual

XCTAssertEqual has the following declaration:

func XCTAssertEqual<T>(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, accuracy: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) where T : FloatingPoint

The swift playground throws the following error:

Generic parameter 'T' could not be inferred

I realize that this assignment isn't particularly "valuable", but I may want to alias other functions with generic type parameters in the future, and would like to know more about any Swift-specific conventions.


Solution

  • The error message is quite clear on what you need to do here - tell the compiler what type T should be. Unfortunately, you can't have a "generic variable" that has an unbound T.

    To do this, you need to write out the full type name of the function XCTAssertEquals<T>, which, for the case of T == Double, is:

    (@autoclosure () throws -> Double, @autoclosure () throws -> Double, Double, @autoclosure () -> String, StaticString, UInt) -> ()
    

    So you need:

    let assertEqual: (@autoclosure () throws -> Double, @autoclosure () throws -> Double, Double, @autoclosure () -> String, StaticString, UInt) -> () = XCTAssertEqual
    

    I know, it's a mess. So if you are going to do this for various Ts, you can first do a type alias for the long function name:

    typealias XCTAssertEqualsType<T> = (@autoclosure () throws -> T, @autoclosure () throws -> T, T, @autoclosure () -> String, StaticString, UInt) -> ()
    

    And then you can use XCTAssertEqualsType<Double> and XCTAssertEqualsType<Float> etc.


    But honestly though, I don't see why you want to alias this assert function. You lose a lot of its features as a function. You'd have to manually pass in the file name and line number "magic" parameters if you call it via your variable. You lose all the optional arguments, and as I said at the start, you lose the generics.

    If all you want is a different name for the function, maybe just declare another function yourself:

    func anotherName<T>(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, accuracy: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) where T : FloatingPoint {
        XCTAssertEqual(try expression1(), try expression2(), accuracy: accuracy, message(), file: file, line: line)
    }