Reputation: 18950
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 AlgebraicExpr
s. 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}
}
}
Upvotes: 0
Views: 272
Reputation: 53142
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> = ...
Upvotes: 3