how to solve “Protocol … can only be used as a generic constraint…”

[亡魂溺海] 提交于 2019-12-12 04:17:41

问题


Consider this fragment of code:

public protocol Evaluable {
    typealias ReturnType
    func eval(s: State) -> ReturnType
}

protocol AlgebraicExpr : Evaluable {
}

public struct Const : AlgebraicExpr {
    var value: Int = 0

    public func eval(s: State) -> Int {
        return value
    }
}

public struct Add : AlgebraicExpr {
    let a, b: AlgebraicExpr

    public func eval(s: State) -> Int {
        return a.eval() + b.eval()
    }
}

it is invalid because Add cannot have an AlgebraicExpr variable.

But I really need Add to store two AlgebraicExprs. How would you solve this in Swift?

For completeness, State is just a struct, for example:

public struct State {
    var env = Dictionary<String, Int>()

    init(_ d: Dictionary<String, Int> = [:]) {
        env = d
    }

    subscript(s: String) -> Int? {
        get {return env[s]}
        set {env[s] = newValue}
    }
}

回答1:


You can't store two protocols that rely on Self, or a typealias because when abstracted as a protocol, the functionality is undetermined. Type Evaluable has no real meaning without understanding the value of ReturnType, so it's not possible for the system to function logically and understand what should be returned.

Take for instance something like this (not valid)

let thing: Evaluable = ...
let result = thing.eval(state) // What should return type be?

The return type can't be inferred through the protocol alone, it requires an implementation by a specified type that gives more knowledge about its behavior by defining the typealias ReturnType

The reason these can function as generic constraints is that the implementation can be inferred through the generic parameters.

func genericConstrained<T: Evaluable>(evaluable: T, state: State) -> T.ReturnType {
    return evaluable.eval(state)
}

In this example, when we call genericConstrained on a particular conforming object, all of its other functionality can be inferred.

tl;dr

If however, you also constrain your package, you could do as you wish:

public struct Add<ExpressionType : AlgebraicExpr> : AlgebraicExpr {
    let a, b: ExpressionType

    public func eval(s: State) -> ExpressionType.ReturnType {
        return a.eval() + b.eval()
    }
}

Then use like

let add: Add<SomeConformingType> = ...


来源:https://stackoverflow.com/questions/34755436/how-to-solve-protocol-can-only-be-used-as-a-generic-constraint

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!