Search code examples
swiftgenericstypesprotocolsinference

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.

enter image description here

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

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

Solution

  • 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.

    E.g.:

    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 w.r.t. concrete versions of the generic type. If viewing the wrapped findPath from a viewpoint external to the owning type, the functions is generic w.r.t. the generic typeholder of the owning type.

    E.g.:

    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)