The result of flipping a triangle is a value of type Shape , and the protoFlip(_:) function takes an argument of some type that conforms to the Shape protocol. However, a value of a boxed protocol type doesn’t conform to that protocol; the value returned by protoFlip(_:) doesn’t conform to Shape . This means code like protoFlip(protoFlip(smallTriangle)) that applies multiple transformations is invalid because the flipped shape isn’t a valid argument to protoFlip(_:).
Which corroborates that any SomeProtocol types do not conform to SomeProtocol, without explaining why, but it also says prior to that in the same section:
protoFlip(_:) [...] always returns a value of the same type. [...] protoFlip(_:) returns [aren't] required to always have the same type — it just has to conform to the Shape protocol.
It seems that the value returned by protoFlip(_:) does conform to the Shape protocol, but obviously this understanding is incorrect, does anyone know why any Shape does not conform to Shape?
func flip(_ shape: any Shape) -> some Shape {
return FlippedShape(shape: shape)
}
An opaque result type always has a single underlying type. At most, it can depend on the generic context of the function you get it from.
For example
func makeValue() -> some Equatable { 42 }
let valOne = makeValue()
let valTwo = makeValue()
valOne == valTwo // âś… Valid because we know valOne and valTwo have same type.
func makeValue<T>(_: T.Type) -> some Equatable { 42 }
let valOne = makeValue(Int.self)
let valTwo = makeValue(String.self)
valOne == valTwo // ❌ Invalid because makeValue<Int>() conceptually returns a different type to makeValue<String>()
let valThree = makeValue(Int.self)
valOne == valThree // âś… Valid because we know valOne and valThree have same type.
In your case, the flip function is not generic - its parameter is an existential, potentially containing "any shape". And yet the result will have a different type depending on which value you call it with - perhaps a FlippedShape<Rectangle> one time, and FlippedShape<Circle the next.
That is why the result cannot be expressed as some Shape and must instead be any Shape.
That's really what it comes down to. Otherwise, the language does let you unbox the existential, and create a FlippedShape of it, but the result needs to be erased back to an any Shape.