Skip to content

Latest commit

ย 

History

History
310 lines (254 loc) ยท 14.1 KB

Opaque Types.md

File metadata and controls

310 lines (254 loc) ยท 14.1 KB

Opque Types - Swift Language Guide

ํ•จ์ˆ˜๋‚˜ ๋ฉ”์†Œ๋“œ์—์„œ opaque return type์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋ฆฌํ„ด ๊ฐ’์˜ ํƒ€์ž… ์ •๋ณด๋ฅผ ์ˆจ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜์˜ ๋ฆฌํ„ดํƒ€์ž…์— ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์„ ๋ช…์‹œํ•˜๋Š” ๋Œ€์‹  ํ•ด๋‹น ํƒ€์ž…์ด ๋”ฐ๋ฅด๋Š” ํ”„๋กœํ† ์ฝœ์„ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌํ„ดํƒ€์ž…์„ ์ˆจ๊ธฐ๋Š” ๊ฒƒ์€ ๋ชจ๋“ˆ๊ณผ ๋ชจ๋“ˆ์„ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ ์‚ฌ์ด์—์„œ ํ•ด๋‹น ๋ฆฌํ„ด ๊ฐ’์„ private ํ•˜๊ฒŒ ๋‘˜ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฆฌํ„ดํƒ€์ž…์„ ํ”„๋กœํ† ์ฝœ ํƒ€์ž…์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ๊ณผ๋Š” ๋‹ฌ๋ฆฌ opque type์€ ํƒ€์ž…์˜ identity๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.(module์˜ ํด๋ผ์ด์–ธํŠธ๋Š” ์ ‘๊ทผํ•  ์ˆ˜ ์—†์ง€๋งŒ ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ํƒ€์ž… ์ •๋ณด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

The Problem That Opaque Types Solve

์˜ˆ๋ฅผ ๋“ค์–ด, ์•„์Šคํ‚ค ์ฝ”๋“œ๋กœ ๋„ํ˜•์„ ๋งŒ๋“œ๋Š” ๋ชจ๋“ˆ์„ ์ฝ”๋”ฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ์•„์Šคํ‚ค ๋„ํ˜• ์—๋Š” draw()ํ•จ์ˆ˜๊ฐ€ ์žˆ์–ด์„œ ๋„ํ˜•์˜ ๋ชจ์–‘์„ string์œผ๋กœ ๋ฆฌํ„ดํ•ด์ค๋‹ˆ๋‹ค. ์ด๋Š” Shape ํ”„๋กœํ† ์ฝœ์— ๋ช…์‹œ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

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())
// *
// **
// ***

์•„๋ž˜์™€ ๊ฐ™์ด ์ œ๋„ค๋ฆญ์„ ์ด์šฉํ•˜์—ฌ ๋„ํ˜•์„ ์œ„ ์•„๋ž˜๋กœ ๋’ค์ง‘์„ ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ์ ‘๊ทผ ๋ฐฉ์‹์—๋Š” ์ค‘๋Œ€ํ•œ ์ œ์•ฝ์ด ์žˆ๋‹ค. flipped result๊ฐ€ ๋งŒ๋“ค์–ด์งˆ ๋•Œ ์ œ๋„ค๋ฆญ์ด ๋…ธ์ถœ๋  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค. However, thereโ€™s an important limitation to this approach: The flipped result exposes the exact generic types that were used to create it. (์ธ์ž๋กœ ํƒ€์ž…์„ ๋„ฃ์–ด์ค˜์„œ ๊ทธ๋Ÿฐ๊ฑด๊ฐ€? opaque๋Š” ์•ˆ๊ทธ๋Ÿฐ๊ฐ€?)

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())
// ***
// **
// *

(์ด ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์ฒ˜๋Ÿผ ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ ์ค‘์ฒฉํ•ด์„œ ์“ธ ๋•Œ ์ œ๋„ค๋ฆญ์ด ์—„์ฒญ ๋งŽ์•„์ง„๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋Š”๋“ฏํ•˜๋‹ค) ์•„๋ž˜์˜ JoinedShape<T: Shape, U: Shape> ๋ฅผ ํ†ตํ•ด 2๊ฐœ์˜ shape๋ฅผ ํ•ฉ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, flipped triangle๊ณผ ๋‹ค๋ฅธ triangle์„ ํ•ฉ์นœ๋‹ค๋ฉด JoinedShape<FlippedShape<Triangle>, Triangle> ์ฒ˜๋Ÿผ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

This approach to defining a JoinedShape<T: Shape, U: Shape> structure that joins two shapes together vertically, like the code below shows, results in types like JoinedShape<FlippedShape<Triangle>, Triangle> from joining a flipped triangle with another triangle.

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())
// *
// **
// ***
// ***
// **
// *

๋„ํ˜• ์ƒ์„ฑ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์ •๋ณด๋ฅผ ๋…ธ์ถœํ•˜๋ฉด ์ „์ฒด ๋ฐ˜ํ™˜ ์œ ํ˜•์„ ๋ช…์‹œํ•ด์•ผ ํ•˜๋ฏ€๋กœ ASCII ์•„ํŠธ ๋ชจ๋“ˆ์˜ public ์ธํ„ฐํŽ˜์ด์Šค์— ํฌํ•จ๋˜์ง€ ์•Š๋Š” ์œ ํ˜•์ด ๋ˆ„์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“ˆ ๋‚ด๋ถ€์˜ ์ฝ”๋“œ๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ๋™์ผํ•œ ๋ชจ์–‘์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ชจ์–‘์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“ˆ ์™ธ๋ถ€์˜ ๋‹ค๋ฅธ ์ฝ”๋“œ๋Š” ๋ณ€ํ™˜ ๋ชฉ๋ก์— ๋Œ€ํ•œ ๊ตฌํ˜„ ์„ธ๋ถ€ ์ •๋ณด๋ฅผ ๊ณ ๋ คํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. JoinedShape๋‚˜ FlippedShape์™€ ๊ฐ™์€ Wrapper type๋“ค์€ module ์‚ฌ์šฉ์ž์—๊ฒŒ ์ค‘์š”ํ•˜์ง€ ์•Š์œผ๋ฉด ๋ณด์ด์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“ˆ์˜ public interface์€ join๊ณผ flip์— ๊ฐ™์€ ๋™์ž‘๋“ค๋กœ ๊ตฌ์„ฑ๋˜์–ด์•ผ ํ•˜๊ณ  ์ด ๋™์ž‘๋“ค์€ ๋˜ ๋‹ค๋ฅธ Shapeํƒ€์ž…์„ ๋ฆฌํ„ดํ•ด์•ผ ํ•œ๋‹ค.

Returning an Opaque Type

opaque type์€ ์ œ๋„ค๋ฆญ์˜ reverse ๋ฒ„์ „์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์ œ๋„ค๋ฆญ ํƒ€์ž…๋“ค์€ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ํ•ด๋‹น ํ•จ์ˆ˜์˜ ๋งค๊ฐœ ๋ณ€์ˆ˜์— ๋Œ€ํ•œ ํƒ€์ž…์„ ์„ ํƒํ•˜๊ณ  ํ•จ์ˆ˜ ๊ตฌํ˜„์—์„œ ์ถ”์ƒํ™”๋œ ๋ฐฉ์‹์œผ๋กœ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด, ์•„๋ž˜ ํ•จ์ˆ˜์˜ ๋ฆฌํ„ด๊ฐ’์€ ๋“ค์–ด์˜ค๋Š” ์ธ์ž์— ๋‹ฌ๋ ค์žˆ์Šต๋‹ˆ๋‹ค.

func max<T>(_ x: T, _ y: T) -> T where T: Comparable { ... }

max(::)ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ์—์„œ x์™€ y๊ฐ’์„ ๊ณ ๋ฅด๋ฉด T์— ํ•ด๋‹นํ•˜๋Š” ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์ด ๊ฒฐ์ •๋œ๋‹ค. ํ˜ธ์ถœํ•˜๋Š”์ชฝ์—์„œ๋Š” Comparable์„ ๋”ฐ๋ฅด๋Š” ์–ด๋–ค ํƒ€์ž…๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ๋Š” generalํ•˜๊ฒŒ ์ฝ”๋“œ๊ฐ€ ์ž‘์„ฑ๋˜์–ด์„œ ๋„˜๊ฒจ์ฃผ๋Š” ์ชฝ์—์„œ ์–ด๋–ค ํƒ€์ž…์„ ๋„˜๊ฒจ์ฃผ์–ด๋„ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋‹ค. max(::)์˜ ๊ตฌํ˜„์€ ๋ชจ๋“  Comparable ํƒ€์ž…์ด ๊ฐ–๊ณ  ์žˆ๋Š” ํƒ€์ž…๋“ค์˜ ๊ธฐ๋Šฅ๋งŒ์„ ์‚ฌ์šฉํ•œ๋‹ค.

์ด๋Ÿฐ ์—ญํ• ๋“ค์€ opaque return type์—์„œ reverse๋˜์–ด์ง„๋‹ค. An opaque type lets the function implementation pick the type for the value it returns in a way thatโ€™s abstracted away from the code that calls the function. ์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜ ํ•จ์ˆ˜๋Š” ์‚ฌ๋‹ค๋ฆฌ๊ผด(trapezoid)๋ฅผ ๋ฆฌํ„ดํ•˜๋Š”๋ฐ ๋ฆฌํ„ด๊ฐ’์ด ์–ด๋–ค ํƒ€์ž…์ธ์ง€ ๋…ธ์ถœ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

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 {
    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())
// *
// **
// **
// **
// **
// *

makeTrapezoid() ํ•จ์ˆ˜์˜ ๋ฆฌํ„ดํ˜•์€ some Shape์ด๋‹ค. ์ด๋กœ์ธํ•ด, return ํƒ€์ž…์€ ํŠน์ • ๊ตฌ์ฒด ํƒ€์ž…์ด ์•„๋‹Œ Shape protocol์„ ๋”ฐ๋ฅด๋Š” ํƒ€์ž…์ด๋‹ค. ๋”ฐ๋ผ์„œ public interface์— ํ•„์š”ํ•œ ์ค‘์š” ์ •๋ณด๋งŒ์„ ๋…ธ์ถœ์‹œํ‚ค๊ณ  ํŠน์ • ํƒ€์ž…์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๋…ธ์ถœ์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค. ์ด ๊ตฌํ˜„์€ 2๊ฐœ์˜ ์‚ผ๊ฐํ˜•๊ณผ 1๊ฐœ์˜ ์‚ฌ๊ฐํ˜•์„ ์ด์šฉํ•œ๋‹ค. ํ•˜์ง€๋งŒ ํ•จ์ˆ˜๋Š” ๋ฆฌํ„ดํƒ€์ž…์„ ๋ฐ”๊พธ์ง€ ์•Š๊ณ  ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ตฌํ˜„์ด ๋ฐ”๋€” ์ˆ˜ ์žˆ๋‹ค.

์ด ์˜ˆ๋Š” opaque return type์ด ์ œ๋„ค๋ฆญ์˜ reverseํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์ค€๋‹ค. makeTrapezoid()๋Š” SHape ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅด๋Š” ์–ด๋–ค ๋ฆฌํ„ดํƒ€์ž…๋„ ๋ฆฌํ„ดํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๊ฒƒ์€ ๋งˆ์น˜ ์ œ๋„ค๋ฆญํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ถ€๋ถ„๊ณผ ๋น„์Šทํ•˜๋‹ค. ์œ„ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ๋Š” generalํ•˜๊ฒŒ ์“ฐ์—ฌ์ ธ์•ผ ํ•œ๋‹ค.(๋งˆ์น˜ generic ํ•จ์ˆ˜์˜ ๊ตฌํ˜„๋ถ€์ฒ˜๋Ÿผ) ๊ทธ๋ž˜์„œ ๋‹ค๋ฅธ ์–ด๋–ค Shape ๊ฐ’๊ณผ๋„ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ ์•„๋ž˜ ํ•จ์ˆ˜์ฒ˜๋Ÿผ opaque return type๊ณผ generic์„ ๊ฐ™์ด ์“ธ ์ˆ˜๋„ ์žˆ๋‹ค.

์ œ๋„ค๋ฆญ์€ ํ•จ์ˆ˜์˜ ์‹œ์ž‘์„ generalํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค๋ฉด opaque return type์€ ํ•จ์ˆ˜์˜ ๋์„ generalํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

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())
// *
// **
// ***
// ***
// **
// *

์ด ์˜ˆ์ œ์˜ opaqueJoinedTriangles๋Š” ์œ„์˜ joinedTriangles์™€ ๊ฐ™์€ ์—ญํ• ์„ ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์œ„์˜ ์˜ˆ์ œ์™€๋Š” ๋‹ฌ๋ฆฌ flip(:)๊ณผ join(:_:)์€ ํ•ด๋‹น ํƒ€์ž…์„ ํ•œ๋ฒˆ ๊ฐ์‹ธ์„œ opaque return type์œผ๋กœ ๋ฆฌํ„ดํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ค‘๊ฐ„์— ๋“ฑ์žฅํ•˜๋Š” ํƒ€์ž…์„ ๋ณด์ด์ง€ ์•Š๊ฒŒ ํ•ด์ค€๋‹ค. ๋‘ ํ•จ์ˆ˜ ๋ชจ๋‘ ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜์ธ๋ฐ ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ํ•ด๋‹น ์ •๋ณด๋ฅผ ํ•„์š”๋กœ ํ•˜๋Š” FlippedShape์™€ JoinedShape์—๊ฒŒ ์ „๋‹ฌ๋œ๋‹ค.

๋งŒ์•ฝ opaque return type์„ ๊ฐ–๊ณ  ์žˆ๋Š” ํ•จ์ˆ˜๊ฐ€ ๋ฆฌํ„ดํ•˜๋Š” ๋ถ€๋ถ„์ด ์—ฌ๋Ÿฌ๊ณณ์— ์žˆ๋‹ค๋ฉด, ํ•ญ์ƒ ๊ฐ™์€ ํƒ€์ž…์„ ๋ฆฌํ„ดํ•ด์•ผํ•œ๋‹ค. ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜์˜ ๊ฒฝ์šฐ ๋ฆฌํ„ดํƒ€์ž…์— ์ œ๋„ค๋ฆญ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋ชจ๋‘ ๊ฐ™์€ ํƒ€์ž…์ด์–ด์•ผ ํ•œ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด ์•„๋ž˜์™€ ๊ฐ™์€ ํ•จ์ˆ˜๋Š” ์—๋Ÿฌ๋ฅผ ๋˜์ง„๋‹ค.

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
}

์—๋Ÿฌ๋ฅผ ์•ˆ๋‚˜๊ฒŒ ํ•˜๋ ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ FlippedShape๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ๋œ๋‹ค.

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์ด ์ œ๋„ค๋ฆญ์„ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. Hereโ€™s an example of a function that incorporates its type parameter into the underlying type of the value it returns:

func `repeat`<T: Shape>(shape: T, count: Int) -> some Collection {
    return Array<T>(repeating: shape, count: count)
}

[T]๊ฐ€ ํ•ญ์ƒ ๋‹ค๋ฅธ ํƒ€์ž…์ผ ์ˆ˜ ์žˆ์ง€๋งŒ some Collection์„ ํ†ตํ•ด ํ•˜๋‚˜์˜ ํƒ€์ž…์œผ๋กœ ๋ฆฌํ„ดํ•  ์ˆ˜ ์žˆ๋‹ค.

In this case, the underlying type of the return value varies depending on T: Whatever shape is passed it, repeat(shape:count:) creates and returns an array of that shape. Nevertheless, the return value always has the same underlying type of [T], so it follows the requirement that functions with opaque return types must return values of only a single type.

Differences Between Opaque Types and Protocol Types

opaque type์œผ๋กœ ๋ฆฌํ„ดํ•˜๋Š” ๊ฒƒ์€ protocol type์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋งค์šฐ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ์ด 2๋ฐฉ์‹์— ์ฐจ์ด์ ์€ ํƒ€์ž…์˜ identity๋ฅผ ๋ณด์กดํ•˜๋Š” ์ง€์ด๋‹ค. opaque type์€ ํŠน์ • ํƒ€์ž…์„ ์•Œ๋ ค์ค€๋‹ค. ๋น„๋ก ์ด opaque return type์„ ๊ฐ–๊ณ ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ชฝ์—์„œ๋Š” ๋ณผ ์ˆ˜ ์—†๋‹ค. ๋ฐ˜๋ฉด protocol type์€ ํ•ด๋‹น protocol์„ ๋”ฐ๋ฅด๋Š” ์–ด๋– ํ•œ ํƒ€์ž…๋„ ๊ฐ€๋ฆฌํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ๋งํ•ด์„œ protocol type์€ ๋” ์œ ์—ฐํ•˜๊ฒŒ ์—ฌ๋Ÿฌ ํƒ€์ž…๋“ค์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๊ณ  opaque type์€ ํŠน์ • ํƒ€์ž…์„ ๊ฐ–๊ฒŒ ํ•ด์ค€๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜ ํ•จ์ˆ˜์—์„œ opaque type ๋Œ€์‹  protocol type์„ ๋ฆฌํ„ดํ•œ๋‹ค.

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

์—ฌ๊ธฐ protoFlip๊ณผ ์œ„์˜ flip์€ ๊ฐ™์€ ๋ฐ”๋””๋ฅผ ๊ฐ–๊ณ  ์žˆ๊ณ  ๋˜‘๊ฐ™์€ ํƒ€์ž…์„ ๋ฆฌํ„ดํ•œ๋‹ค. flip๊ณผ๋Š” ๋‹ฌ๋ฆฌ protoType์€ Shape ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅด๋Š” ๊ทธ ์–ด๋–ค๊ฒƒ๋„ ๋ฆฌํ„ดํ•  ์ˆ˜ ์žˆ๋‹ค. protoFlip์€ flip๋ณด๋‹ค ๋” ๋Š์Šจํ•˜๊ฒŒ API ์ƒํ˜ธ์ž‘์šฉ์„ ํ•˜๊ฒŒ ๋œ๋‹ค. ์—ฌ๋Ÿฌ ํƒ€์ž…์„ ๋ฆฌํ„ดํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์—ฐ์„ฑ์„ ๊ฐ–๊ฒŒ ๋œ๋‹ค.

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

    return FlippedShape(shape: shape)
}

์ด ๋ฒ„์ „์€ Square๋‚˜ FlippedShape๋ฅผ ๋ฆฌํ„ดํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๋Š” ์–ด๋–ค ๋„ํ˜•์ด ์ธ์ž๋กœ ๋“ค์–ด์˜ค๋Š”์ง€์— ๋‹ฌ๋ ธ๋‹ค. ์ด ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋งŒ๋“ค์–ด์ง„ 2๊ฐœ์˜ flipped shape๋Š” ์™„์ „ํžˆ ๋‹ค๋ฅธ ํƒ€์ž…์ด ๋  ์ˆ˜๋„ ์žˆ๋‹ค. protoFlip์—์„œ์ฒ˜๋Ÿผ ๋” ์ ์€ ์ •๋ณด๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” ๊ฒƒ์€ ํƒ€์ž… ์ •๋ณด์— ๋”ฐ๋ผ ๋™์ž‘ํ•˜๋Š” ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ์„ ์˜๋ฏธํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•„๋ž˜ == operator๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

let protoFlippedTriangle = protoFlip(smallTriangle)
let sameThing = protoFlip(smallTriangle)
protoFlippedTriangle == sameThing  // Error

๋งˆ์ง€๋ง‰ ์ค„์˜ ์—๋Ÿฌ๋Š” ๋ช‡๊ฐ€์ง€ ์ด์œ  ๋•Œ๋ฌธ์ด๋‹ค. Shape ํ”„๋กœํ† ์ฝœ์—๋Š” == operator๊ฐ€ ๋ช…์‹œ๋˜์ง€ ์•Š์•˜๋‹ค. == operator๋ฅผ ์ถ”๊ฐ€ํ•˜๋ ค๊ณ  ํ•œ๋‹ค๋ฉด ๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๋ฅผ ๋งŒ๋‚˜๊ฒŒ ๋˜๋Š”๋ฐ ์ด๋Š” lhs์™€ rhs์˜ ํƒ€์ž…์„ ์•Œ์•„์•ผ ํ•œ๋‹ค. ์ด๋Ÿฐ ์ข…๋ฅ˜์˜ operator๋Š” Self ํƒ€์ž…์„ ์ธ์ž๋กœ ๋ฐ›๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ข…์ข… ์žˆ๋‹ค. ์ด Self๋Š” ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅด๋Š” ๊ตฌ์ฒด ํƒ€์ž…๊ณผ ๋งค์นญ๋œ๋‹ค.

ํ”„๋กœํ† ์ฝœ ํƒ€์ž…์„ ๋ฆฌํ„ดํƒ€์ž…์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ํ•ด๋‹น ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅด๋Š” ์–ด๋–ค ํƒ€์ž…๋„ ๋ฆฌํ„ดํƒ€์ž…์ด ๋  ์ˆ˜ ์žˆ๋Š” ์œ ์—ฐํ•จ์„ ์ œ๊ณตํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ ์œ ์—ฐํ•จ์˜ ๋Œ“๊ฐ€๋กœ ๋ฆฌํ„ด ํƒ€์ž…์˜ ํŠน์ • ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค. ์ด ์—๋ฅผ ์–ด๋–ป๊ฒŒ == operator๊ฐ€ ์‚ฌ์šฉ๋˜์ง€ ๋ชปํ•˜๋Š”์ง€๋ฅผ ๋ณด์—ฌ์ค€๋‹ค. ์ด๋Š” ํ”„๋กœํ† ์ฝœ ํƒ€์ž…์— ์˜ํ•ด ๋ณด์กด๋˜์ง€ ์•Š๋Š” ํŠน์ • ํƒ€์ž…์ •๋ณด์— ๋‹ฌ๋ ค์ž‡๋‹ค.(ํ”„๋กœํ† ์ฝœ์— ๋ช…์‹œ๋˜์ง€ ์•Š์œผ๋ฉด ๋ชป์“ด๋‹ค.)

์ด ํ”„๋กœํ† ์ฝœ ํƒ€์ž… ๋ฐฉ์‹์˜ ๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๋Š” shape์˜ ๋ณ€ํ™˜์ด ์ค‘์ฒฉ๋˜์ง€ ์•Š๋Š”๋‹ค. ์‚ผ๊ฐํ˜•์˜ flipping ๊ฒฐ๊ณผ๋Š” Shape ํƒ€์ž…์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  protoFlip์€ Shape protocol์„ ๋”ฐ๋ฅด๋Š” ํƒ€์ž…์„ ์ธ์ž๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, ํ”„๋กœํ† ์ฝœ ํƒ€์ž…์˜ ๊ฐ’์€ protocol์„ ๋”ฐ๋ฅด์ง€ ์•Š๋Š”๋‹ค. ์ฆ‰ protoFlip์˜ ๋ฆฌํ„ด๊ฐ’์€ Shape protocol์„ ๋”ฐ๋ฅด์ง€ ์•Š๋Š”๋‹ค. ์ด ๋ง์€ protoFlip(protoFlip(smallTriangle))๊ณผ ๊ฐ™์€ ์ค‘์ฒฉ ๋ณ€ํ™˜์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

์ด์™€๋Š” ๋‹ฌ๋ฆฌ opaque type์€ ํ•ด๋‹น ํƒ€์ž…์˜ identity๋ฅผ ๋ณด์กดํ•œ๋‹ค. ์Šค์œ„ํ”„ํŠธ๋Š” associated types์„ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๋กœ์จ opaque return value๋ฅผ ๋ฆฌํ„ด ๊ฐ’์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.(ํ”„๋กœํ† ์ฝœ ํƒ€์ž…์€ ๋ถˆ๊ฐ€๋Šฅํ•œ๋ฐ ๋ฐ˜ํ•ด)

protocol Container {
    associatedtype Item
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}
extension Array: Container { }

Container๋ฅผ ํ•จ์ˆ˜์˜ ๋ฆฌํ„ดํƒ€์ž…์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ์™œ๋ƒํ•˜๋ฉด ํ”„๋กœํ† ์ฝœ์€ associatedtype์„ ๊ฐ–๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

// 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]
}

opaque type์„ ๋ฆฌํ„ด ํƒ€์ž…์œผ๋กœ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ API์˜ ์š”๊ตฌํ•˜๋Š”๋Œ€๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์ž‡๋‹ค.

func makeOpaqueContainer<T>(item: T) -> some Container {
    return [item]
}
let opaqueContainer = makeOpaqueContainer(item: 12)
let twelve = opaqueContainer[0]
print(type(of: twelve))
// Prints "Int"

twelve์˜ ํƒ€์ž…์€ Int๊ฐ€ ๋œ๋‹ค. ์™œ๋ƒํ•˜๋ฉด opaque type์—์„œ๋Š” ํƒ€์ž… ์ถ”๋ก ์ด ์ผ์–ด๋‚˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. makeOpaqueContainer(item:)์—์„œ ํ‘œํ˜„๋˜๋Š” opaque container๋Š” [T]์ด๋‹ค. ์—ฌ๊ธฐ์„œ T๋Š” Int๊ฐ€ ๋˜๊ฒŒ ๋˜๋Š”๋ฐ ๊ทธ๋ฆฌํ•˜์—ฌ Int๋ฐฐ์—ด์˜ ๋ฆฌํ„ดํ˜•๊ณผ associated type์ธ Item์˜ ์ž๋ฃŒํ˜•์€ Int๋กœ ์ถ”๋ก ๋œ๋‹ค. Container์˜ subscript๋Š” Item์„ ๋ฆฌํ„ดํ•˜๊ณ  ์ด๋Š” Int๋ฅผ ์˜๋ฏธํ•œ๋‹ค.