Why does the compiler not see the default code in a protocol?

Tchelyzt

Edit: I have restated and hopefully clarified this question over here. Now I've added the solution.

I've defined a function (see foo() in attached example) as a default function for structs adopting my protocol. It applies the + operator defined in respect of two other variables which themselves adopt other protocols and + is defined in one of those protocols. The variables are typed using associatedtypes. I get the message:

Binary operator '+' cannot be applied to operands of type 'Self.PointType' and 'Self.VectorType'

If I implement the function inside my struct (see bar() in attached) it works so I'm sure my + operator does work. My example is pared down to the minimum needed to work in a playground. Just remove the comments in the LineProtocol extension to get the error. It seems to me that Self.PointType is a Point and Self.VectorType is a Vector.

To be clear: The reason I used associatedtypes is because many different structs adopt each of the three protocols in the example so I can't name them directly

public protocol 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())

Solution adapted from @Honey's suggestion: replace extension with:

extension LineProtocol where Self.VectorType == Self.PointType.VectorType {
   public func foo() -> PointType {
      // Constraint passes VectorType thru to the PointProtocol
      return (anchor + direction)
   }
}

Honey

I know what the problem is. Not sure if my solution is the best answer.

The problem is that both your associatedtypes have associatedtypes themselves.

So in the extension, the Swift compiler can't figure out the type of the associatedtypes — unless you constrain it.

Like do:

extension LineProtocol where Self.VectorType == Vector, Self.PointType == Point {
    public func foo() -> Self.PointType {
      return (anchor + direction)
   }
}

Your code works for your concrete type Line, because both your associatedtypes have their requirements fulfilled ie:

public typealias PointType = Point // makes compiler happy!
public typealias VectorType = Vector  // makes compiler happy!

FWIW you could have got rid of the explicit conformance to your associatedtype requirements and let the compiler infer1 conformance to your associatedtypes requirements and write your Line type as such:

public struct Line: LineProtocol {

   public var anchor: Point
   public var direction: Vector

   public init(anchor: Point, direction: Vector) {
      self.anchor = anchor
      self.direction = direction
   }

   public func bar() -> Point {
      return (anchor + direction)
   }
}

1: Generics - Associated Types

Thanks to Swift’s type inference, you don’t actually need to declare a concrete Item of Int as part of the definition of IntStack. Because IntStack conforms to all of the requirements of the Container protocol, Swift can infer the appropriate Item to use, simply by looking at the type of the append(_:) method’s item parameter and the return type of the subscript. Indeed, if you delete the typealias Item = Int line from the code above, everything still works, because it’s clear what type should be used for Item.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Why does the compiler complain that 'not all code paths return a value' when I can clearly see that they do?

see why "type does not conform to protocol" in Xcode (swift)

Why compiler does not see method of base class when using CRTP

Why does the compiler not optimize away interrupt code?

Why does the C# compiler crash on this code?

Why Comparable protocol does not have default implementation of Equatable?

Why does the Scala compiler disallow overloaded methods with default arguments?

Why does the compiler need an implementation of a trait to call a default free function?

How can I see in what [Java/Scala?] code does Scala compiler rewrites original Scala-code

Why does the Eclipse code formatter break </a> in a Javadoc @see tag?

Why does my GCC compiler not compile C code?

Why does compiler generate additional sqrts in the compiled assembly code

Why does compiler build return unreachable code in some cases

Why does this Kotlin code with generics produce a Type Mismatch compiler error?

Why does compiling this code cause a compiler stack overflow?

why does the compiler give error on the following haskell code?

Why does this code give a "Possible null reference return" compiler warning?

why does this code compile with eclipse compiler but not with javac (maven)

Why does c# compiler leave il code for conditional method?

Why TypeScript compiler does not allow this seemingly valid code?

Why does the compiler generate such code when initializing a volatile array?

Why does this source code using "crypt" have this compiler warning:

Why does compiler say it can't see a template it has just generated?

Why does the compiler allow this?

Why the compiler does this division

Why does the protocol default value passed to the function not change, even though the function does when subclassing?

Why can't the compiler see this `From` impl?

Why a compiler do not see a variable in the scope?

How does a compiler see an exception in a multicatch context?

TOP Ranking

HotTag

Archive