ASCII art module을 만든다고 가정해보자.
Opaque Types가 없던시절, 삼각형을 그리는 Triangle struct를 만들어보면 아래와 같다.
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")
}
}
let smallTriangle = Triangle(size: 3)
print(smallTriangle.draw())
// *
// **
// ***
여기서 generics을 이용해서 shape을 뒤집어주는 FlippedShape Struct를 작성하면 아래와 같다.
struct FlippedShape<T: Shape>: Shape {
var shape: T
func draw() -> String {
let lines = shape.draw().split(separator: "\n")
return lines.reversed().joined(separator: "\n")
}
}
let flippedTriangle = FlippedShape(shape: smallTriangle)
print(flippedTriangle.draw())
// ***
// **
// *
이방식에서의 문제 : FilppedShape를 생성하는데 사용되는 generic type을 노출하게된다. (모듈 외부의 클라이언트가 이 타입을 알게된다.)
두개의 삼각형을 위아래로 합치는 JoinedShape Struct를 만들어보면 아래와 같다(위랑 마찬가지로 생성에 필요한 generic type을 노출하게된다.)
struct JoinedShape<T: Shape, U: Shape>: Shape {
var top: T
var bottom: U
func draw() -> String {
return top.draw() + "\n" + bottom.draw()
}
}
let joinedTriangles = JoinedShape(top: smallTriangle, bottom: flippedTriangle)
print(joinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *
[Generic Type]
func max<T>(_ x: T, _ y: T) -> T where T: Comparable { ... }
[Opaque Type]
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 makeTrapezoid() -> some Shape {
//만약 제네리으로 구현했다면 top, middle을 인자로 받아야했을 것이다. 그렇게되면 모듈 사용자는 Triangle, Saqure라는 Type을 (알 필요가없는데도)알아야한다.
let top = Triangle(size: 2)
let middle = Square(size: 2)
let bottom = FlippedShape(shape: top)
let trapezoid = JoinedShape(
top: top,
bottom: JoinedShape(top: middle, bottom: bottom)
)
return trapezoid
}
let trapezoid = makeTrapezoid()
print(trapezoid.draw())
// *
// **
// **
// **
// **
// *
opaque return type이랑 generic을 같이 쓸 수도있다.
func flip<T: Shape>(_ shape: T) -> some Shape {
return FlippedShape(shape: shape)
}
func join<T: Shape, U: Shape>(_ top: T, _ bottom: U) -> some Shape {
JoinedShape(top: top, bottom: bottom)
}
let opaqueJoinedTriangles = join(smallTriangle, flip(smallTriangle))
print(opaqueJoinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *
func invalidFlip<T: Shape>(_ shape: T) -> some Shape {
if shape is Square {
return shape // Error: return types don't match
}
return FlippedShape(shape: shape) // Error: return types don't match
}
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")
}
}
opaque return type에 제네릭을 사용해서 하나의 type을 리턴할수도 있다.
아래는 return타입에 제네릭 타입을 포함하는 function이다.
func `repeat`<T: Shape>(shape: T, count: Int) -> some Collection {
return Array<T>(repeating: shape, count: count)
}
이 경우 리턴값의 underlying type은 T에 의존한다
어떤 shape 이 전달되던 repeat(shape:count:) 은 shape의 array를 생성해서 리턴한다.
항상 같은 [T] 라는 타입을 리턴하기때문에 opaque return type을 쓰면서도 하나의 타입을 리턴하고있다.
func protoFlip<T: Shape>(_ shape: T) -> Shape {
return FlippedShape(shape: shape)
}
func protoFlip<T: Shape>(_ shape: T) -> Shape {
if shape is Square {
return shape
}
return FlippedShape(shape: shape)
}
let protoFlippedTriangle = protoFlip(smallTriangle)
let sameThing = protoFlip(smallTriangle)
protoFlippedTriangle == sameThing // Error
protocol Container {
associatedtype Item
var count: Int { get }
subscript(i: Int) -> Item { get }
}
extension Array: Container { }
// Error: Protocol with associated types can't be used as a return type.
func makeProtocolContainer<T>(item: T) -> Container {
return [item]
}
// Error: Not enough information to infer C.
func makeProtocolContainer<T, C: Container>(item: T) -> C {
return [item]
}
func makeOpaqueContainer<T>(item: T) -> some Container {
return [item]
}
let opaqueContainer = makeOpaqueContainer(item: 12)
let twelve = opaqueContainer[0]
print(type(of: twelve))
// Prints "Int"
https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html