swift-book icon indicating copy to clipboard operation
swift-book copied to clipboard

Nesting Functions returning Protocol Types aren’t “Invalid”

Open bytedriver opened this issue 1 year ago • 0 comments

Location

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/opaquetypes#Differences-Between-Opaque-Types-and-Protocol-Types

Description

In the document, the Chapter "Opaque Types" describes the difference between protocol types and opaque types as function return types as follows:

Another problem with this approach is that the shape transformations don’t nest. 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 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(_:).

However, in practice, unlikely, it is valid as proven by the following example:

#if swift(>=5.8)
print("hello swift 5.8+")
#endif

protocol Shape {
    func draw() -> String
}

struct Triangle: Shape {
    var size: Int
    func draw() -> String {
        var result: [String] = []
        for length in 1...size {
            result.append(String(repeating: "*", count: length))
        }
        return result.joined(separator: "\n")
    }
}

struct FlippedShape<T: Shape>: Shape {
    var shape: T
    func draw() -> String {
        if shape is Square {
            return shape.draw()
        }
        let lines = shape.draw().split(separator: "\n")
        return lines.reversed().joined(separator: "\n")
    }
}

struct Square: Shape {
    var size: Int
    func draw() -> String {
        let line = String(repeating: "*", count: size)
        let result = Array<String>(repeating: line, count: size)
        return result.joined(separator: "\n")
    }
}

func flip<T: Shape>(_ shape: T) -> some Shape {
    return FlippedShape(shape: shape)
}

func protoFlip<T: Shape>(_ shape: T) -> Shape {
    if shape is Square {
        return shape
    }
    return FlippedShape(shape: shape)
}

let smallTriangle = Triangle(size: 3)
let nestedThing = protoFlip(protoFlip(smallTriangle))
print(nestedThing.draw())
// hello swift 5.8+
// *
// **
// ***

Discussion in here: https://forums.swift.org/t/nesting-functions-returning-protocol-types-arent-invalid-in-swift-5-8/64366

Correction

The following phrase sounds confusing: "However, a value of a protocol type doesn’t conform to that protocol."

And it should prompt a compile error.

bytedriver avatar Apr 12 '23 23:04 bytedriver