简体   繁体   中英

Swift Generic Type Inference

So I was wondering if any one could explain the rational behind this error or an explanation of what I am doing wrong.

I am trying to create a generic function which takes a protocol constrained type which has a static method on it called solve.

But for some reason even though it resolves the constraint fine in Xcode the compiler throws a hissy.

Is there any reason it shouldn't be able to infer a type which I am already specifying or is there a naive error in my code sample?

Edit: As it wasnt explicit enough

I know how to work around it, I was just curious for an explanation of why static members in interfaces/ protocols are problematic.

在此处输入图片说明

protocol NodeSolver {
    static func solve(_ nodes: [Node]) -> [Node]
}

func findPath<T: NodeSolver>(nodes: [Node]) -> [Node]  {        
        return T.solve(nodes)
    }

Since it seems you want findPath to be a method strongly connected to the type that conforms to NodeSolver , but wont use any instance of this concrete NodeSolver type in the findPath method itself, you might want to consider simply adding the general findPath method as a type method available by a default implementation for all types conforming to NodeSolver .

Eg:

struct Node {}

protocol NodeSolver {
    static func solve(_ nodes: [Node]) -> [Node]
    static func findPath(nodes: [Node]) -> [Node]
}

extension NodeSolver {
    static func findPath(nodes: [Node]) -> [Node]  {
        // hopefully some more logic ...
        return Self.solve(nodes)
    }
}

struct MyNodeSolver: NodeSolver {
    // dummy solver
    static func solve(_ nodes: [Node]) -> [Node] {
        return nodes
    }
}

let myNodes = [Node(), Node()]

// make use of the default implementation of `findPath` available
// to all types conforming to 'NodeSolver': this method itself
// make use of the concrete type-specific implementation of 'solve'
// in the types conforming to 'NodeSolver'.
let dummyPath = MyNodeSolver.findPath(nodes: myNodes)

I am handing a constrained type in the protocol I am specifying. And the actual type in the method call.

 findPath<NodeSolver1>(nodes) findPath<NodeSolver2>(nodes) 

Another workaround, possible closer to what you're trying to achieve, is to wrap the generic function into a generic type (say, struct ) which holds a non-generic function findPath wrt concrete versions of the generic type. If viewing the wrapped findPath from a viewpoint external to the owning type, the functions is generic wrt the generic typeholder of the owning type.

Eg:

struct Node {}

protocol NodeSolver {
    static func solve(_ nodes: [Node]) -> [Node]
}

struct NodeSolverA: NodeSolver {
    static func solve(_ nodes: [Node]) -> [Node] {
        return nodes
    }
}

struct NodeSolverB: NodeSolver {
    static func solve(_ nodes: [Node]) -> [Node] {
        return nodes.reversed()
    }
}

// instead of a generic function, wrap a concrete function
// in a generic type
struct AnyNodeSolver<T: NodeSolver> {
    static func findPath(nodes: [Node]) -> [Node]  {
        return T.solve(nodes)
    }
}

let myNodes = [Node(), Node()]

let dummyPathA = AnyNodeSolver<NodeSolverA>.findPath(nodes: myNodes)
let dummyPathB = AnyNodeSolver<NodeSolverB>.findPath(nodes: myNodes)

You have to specify the type of T in the function signature.

func findPath<T: NodeSolver>(nodes: [Node], ofType type: T.Type) -> [Node]  {        
    return T.solve(nodes)
}

If you define a generic type you must enable the compiler to somehow infer the actual type. Currently there would be no way to achieve that. How would you make the two calls to that method with different NodeSolver s?

Yannick wrote an answer for specifying the type - you ask "Then what is the point of the generic? If you have to specify it twice?" - you do not have to. General example:

protocol P {
    static func solve(argument : String)
}

class P1 : P {
    class func solve(argument : String) {
        print("first")
    }
}

class P2 : P {
    class func solve(argument : String) {
        print("second")
    }
}


func doSome(argument : String, a : P.Type) {
    a.solve(argument: argument)
}

doSome(argument: "", a: P1.self) // prints "first"
doSome(argument: "", a: P2.self) // prints "second"

In your case:

func findPath(nodes: [Node], solver: NodeSolver.Type) -> [Node]  {        
    return solver.solve(nodes)
}

Talked to a colleague who replied with this (it's in c# but relevant)

and explains the problems with this question in terms of language implementation.

Thanks for all your answers and time.

https://blogs.msdn.microsoft.com/ericlippert/2007/06/14/calling-static-methods-on-type-parameters-is-illegal-part-one/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM