Yesterday I asked a question. This is an attempt to state the question more clearly.
THE CONTEXT:
I have a family of struct
s (called Vector2D, Vector3D, etc) which represent vectors in various dimensions. Their implementations are very similar but since inheritance is not available for struct
s I use a protocol
(VectorProtocol) which enables me to write default member functions as protocol extensions
instead of repeating them for each struct
.
Similarly, I have a family of struct
s (called Point2D, Point3D, etc) which represent points in various dimensions. Their implementations are also very similar so I use another protocol (PointProtocol) for the same reason.
In each case the protocols use associatedtype
s to identify the particular struct
instance type.
Sometimes PointxDs and VectorxDs interact so PointProtocol
also has an associatedtype
to identify the particular VectorxD it needs to handle and VectorProtocol
has an associatedtype
to identify the particular PointxD it needs to handle.
So far, everything works fine, including interaction between points and vectors. For instance the difference between 2 points is a vector and the sum of a point and a vector is a point and I can define operators once to handle these cases for every dimension as follows:
public protocol PointProtocol {
associatedtype VectorType: VectorProtocol
/* etc. */
}
extension PointProtocol {
public static func +(lhs: Self, rhs: VectorType) -> Self { /*…*/ }
public static func -(lhs: Self, rhs: Self) -> VectorType { /*…*/ }
}
THE PROBLEM:
Now I want to add a further family of struct
s (called Line2D, Line3D, etc) which represent lines in various dimensions and which interact with points and vectors. I thought I was doing more of the same but there is a subtle difference. Lines are composed of points and vectors.
public protocol LineProtocol {
associatedtype PointType: PointProtocol
associatedtype VectorType: VectorProtocol
var anchor: PointType { get set }
var direction: VectorType { get set }
}
public struct Line2D: LineProtocol {
public typealias PointType = Point2D
public typealias VectorType = Vector2D
var anchor: PointType
var direction: VectorType
public init(anchor: Point2D, direction:Vector2D) { … }
}
This causes no problem when it comes to constructing, for example, a Line2D using a Point2D and a Vector2D. However, the compiler baulks when it comes to this default declaration:
extension LineProtocol {
public func endOfLineSegment() -> PointType {
return (anchor + direction) // Compiler ERROR
// Binary operator '+' cannot be applied to operands of type
// 'Self.PointType' and 'Self.VectorType'
}
}
It looks like the compiler cannot find the operator declaration public static func +(lhs: PointType, rhs: VectorType) -> PointType
even though anchor
is clearly of type PointProtocol
and direction is clearly of type VectorProtocol
. So I think endOfLineSegment()
knows that anchor and direction are PointType and VectorType respectively, which means it should know that they are PointProtocol and Vector Protocol too, but it does not know how to set the two associatedtype
s for these protocols.
Does anybody know how to fix this? (Without having to implement the function individually in each struct
declaration which does work)
Below find sufficient minimal code to generate the error in a playground
public protocol PointProtocol {
associatedtype PointType: PointProtocol
associatedtype VectorType: VectorProtocol
var elements: [Float] { get set }
}
extension PointProtocol {
public static func +(lhs: Self, rhs:VectorType) -> Self {
var translate = lhs
for i in 0..<2 { translate.elements[i] += rhs.elements[i] }
return translate
}
}
public protocol VectorProtocol {
associatedtype VectorType: VectorProtocol
var elements: [Float] { get set }
}
public struct Point: PointProtocol {
public typealias PointType = Point
public typealias VectorType = Vector
public var elements = [Float](repeating: 0.0, count: 2)
public init(_ x: Float,_ y: Float) {
self.elements = [x,y]
}
}
public struct Vector: VectorProtocol {
public typealias VectorType = Vector
public static let dimension: Int = 2
public var elements = [Float](repeating:Float(0.0), count: 2)
public init(_ x: Float,_ y: Float) {
self.elements = [x,y]
}
}
public protocol LineProtocol {
associatedtype PointType: PointProtocol
associatedtype VectorType: VectorProtocol
var anchor: PointType { get set }
var direction: VectorType { get set }
}
extension LineProtocol {
public func foo() -> PointType {
return (anchor + direction)
}
}
public struct Line: LineProtocol {
public typealias PointType = Point
public typealias VectorType = Vector
public var anchor: PointType
public var direction: VectorType
public init(anchor: Point, direction: Vector) {
self.anchor = anchor
self.direction = direction
}
public func bar() -> Point {
return (anchor + direction)
}
}
let line = Line(anchor: Point(3, 4), direction: Vector(5, 1))
print(line.bar())
//print(line.foo())