SOPT-HIG-WWDC / WWDC

WWDC 스터디
0 stars 0 forks source link

Embrace Swift generics #5

Open kimscastle opened 1 year ago

kimscastle commented 1 year ago

1️⃣WWDC 영상 요약

Generic

Abstraction

let startRadians = startAngle * .pi / 180.0
let endRadians = endAngle * .pi / 180.0
func radians(from degrees: Double) -> Double {
  degrees * .pi / 180.0
}

let startRadians = radians(from: startAngle)
let endRadians = radians(from: endAngle)

Abstraction in swift

<T> where T: Idea

이제부터 아래 예제에서 swift 5.7 Generic 을 활용하는 방법을 알아보자!

  1. 먼저 Concrete Type 으로 구현된 Model 들을 살펴볼 것이다
  2. Concrete Type 들의 공통적인 부분을 찾아보자
  3. Interface 를 만들어보자
  4. Generic code 를 작성해보자

WWDC영상에서 제공하는 코드 : 농장 시뮬레이션을 위한 코드

스크린샷 2023-04-16 오후 12 51 48

Model with concrete types

struct Cow {
    func eat(_ food: Hay) {}
}
struct Hay {
    static func grow() -> Alfalfa {
        return Alfalfa()
    }
}
struct Alfalfa {
    func harvest() -> Hay {
        Hay()
    }
}
struct Farm {
    func feed(_ animal: Cow) {
        let alfalfa = Hay.grow()
        let hay = alfalfa.harvest()
        animal.eat(hay)
    }
}
struct Farm {
    func feed(_ animal: Cow) {
        let alfalfa = Hay.grow()
        let hay = alfalfa.harvest()
        animal.eat(hay)
    }

    func feed(_ animal: Horse) {
        let root = Carrot.grow()
        let carrot = root.harvest()
        animal.eat(carrot)
    }

    func feed(_ animal: Chicken) {
        let wheat = Grain.grow()
        let grain = wheat.harvest()
        animal.eat(grain)
    }
}

Identify common capabilities

Polymorphism - 다형성

다형성의 다양한 형태

  1. function overloading(함수 오버로딩)
    • polymorphism 이라고 불린다
    • argument type 에 따라 같은 메소드 호출이 다른 의미를 가질 때를 말한다.
    • 일반적인 해결책은 아니다
    • Subtype(하위 유형 다형성)
    • Super Type 의 코드가 특정 subtype 에서 다른 동작을 할 때를 말한다.
    • Parametric(매개변수 다형성 - 제네릭 사용)
    • Generic 에 의해 형성되는 polymorphism
    • Generic code 는 타입 파라미터를 사용한다.
    • 타입 파라미터를 통해 여러 가지 타입에서 사용되는 하나의 코드 덩어리를 작성할 수 있다.
    • concrete type 들은 제네릭 타입의 argument 로 사용될 수 있다.

Subtype polymorphism (서브타입 다형성) 을 이용해 위와 같은 문제를 해결해보자

class Animal {
    func eat(_ food: ???) {fatalError("Subclass must implement `eat`")}
}
class Cow: Animal {
    override func eat(_ food: Hay) {}
}
class Horse: Animal {
    override func eat(_ food: Carrot) {}
}
class Chicken: Animal {
    override func eat(_ food: Grain) {}
}

위와 같은 subtype polymorphism 의 단점

첫 번째 방법: Any 사용하기

class Animal {
    func eat(_ food: Any) {fatalError("Subclass must implement `eat`")}
}
class Cow: Animal {
    override func eat(_ food: Any) {
        guard let food = food as? Hay else { fatalError("Cow cannot eat \(food)") }
    }
}
class Horse: Animal {
    override func eat(_ food: Any) {
        guard let food = food as? Carrot else { fatalError("Horse cannot eat \(food)") }
    }
}
class Chicken: Animal {
    override func eat(_ food: Any) {
        guard let food = food as? Grain else { fatalError("Chicken cannot eat \(food)") }
    }
}

두 번째 방법: Generic 사용하기

class Animal<Food> {
    func eat(_ food: Food) {fatalError("Subclass must implement `eat`")}
}
class Cow: Animal<Hay> {
    override func eat(_ food: Hay) {
        ...
    }
}
class Horse: Animal<Carrot> {
    override func eat(_ food: Carrot) {
        ...
    }
}
class Chicken: Animal<Grain> {
    override func eat(_ food: Grain) {
        ...
    }
}
struct Hay {
    static func grow() -> Alfalfa {
        return Alfalfa()
    }
}
class Animal<Food, Habitat, Commodity>

Build Interface

Protocol

스크린샷 2023-04-16 오후 1 09 05

Protocol 을 사용하여 Animal 을 표현해보자

protocol Animal {
  associatedtype Feed: AnimalFeed
  func eat(_ food: Feed)
}

Protocol 을 conform 하는 Animal 타입들을 구현해보자.

protocol Animal {
  associatedtype Feed: AnimalFeed
  func eat(_ food: Feed)
}

struct Cow: Animal {
  func eat(_ food: Hay) { ... }
}
struct Horse: Animal {
  func eat(_ food: Carrot) { ... }
}
struct Chicken: Animal {
  func eat(_ food: Grain) { ... }
}

Write a generic code

protocol Animal {
  associatedtype Feed: AnimalFeed
  func eat(_ food: Feed)
}

struct Farm {
  func feed(_ animal: ???) {...}
}
struct Farm {
  func feed<A>(_ animal: A) where A: Animal {...}
  // or
  func feed<A: Animal>(_ animal:A) {...}
}

타입 파라미터 더욱 간소화하기

func feed(_ animal: some Animal)

What is some?

func feed(_ animal: some Animal)
func feed<A: Animal>(_ animal: A)
func feed(_ animal: some Animal)
func getValue(Parameter) -> Result

Inferring the underlying type for some

let animal: some Animal = Horse()
var animal: some Animal = Horse()
animal = Cow() // Compiler Error 발생! underlying type 이 이미 Horse 로 결정된 상태에서 Cow 로 바꾸려고 시도하기 때문이다.

타입 파라미터는 언제 사용하는게 좋을까?

struct Silo<Material> {
  private var storage: [Material]

  init(storing materials: [Material]) {
    self.storage = materials
  }
}

var hayStorage: Silo<Hay>

이제 다시 generic code 를 작성하러 돌아가자

feed 함수 다시 작성하기

protocol Animal {
  associatedtype Feed: AnimalFeed // <- grow static function 을 가지고 있는 추상화 프로토콜 이라고 생각하자.
  func eat(_ food: Feed)
}

struct Farm {
  func feed(_ animal: some Animal) {
    let crop = type(of: animal).Feed.grow() // Animal 의 associatedtype에 접근하기 위해 type(of:) 를 사용할 수 있다.
    let produce = crop.harvest() // 작물로부터 곡물을 획득한다.
    animal.eat(produce) // opaque animal type 에 곡물을 먹인다.
  }
}
func feed(_ animal: some Animal) {
  let crop = type(of: animal).Feed.grow()
  let produce = crop.harvest()
  animal.eat(Hay.grow().harvest()) // 만약 올바르지 않은 Feed 타입을 먹이려고 한다면, 컴파일러에 의해 에러가 발생한다.
}

마지막으로 모든 동물에게 먹이를 주는 feedAll 함수를 구현해보자!

struct Farm {
  func feed(_ animal: some Animal) {
    let crop = type(of: animal).Feed.grow()
    let produce = crop.harvest()
    animal.eat(produce)
  }

  func feedAll(_ animals: [some Animal]) {

  }
}

any 키워드

func feedAll(_ animals: [any Animal]) {

}
스크린샷 2023-04-16 오후 1 26 47

다시 feedAll 메소드로 돌아가자!

func feedAll(_ animals: [any Animal]) {
  for animal in animals {
    animal.eat(food: Animal.Feed) // 이 부분에서 컴파일 에러가 발생한다!
  }
}
func feed(_ animal: some Animal) {
  let crop = type(of: animal).Feed.grow()
  let produce = crop.harvest()
  animal.eat(produce)
}

func feedAll(_ animals: [any Animal]) {
  for animal in animals {
    feed(animal) // any Animal 을 some Animal 으로 넘겨서 underlying type 을 unboxing 한다.
  }
}
스크린샷 2023-04-16 오후 1 30 34
PecanPiePOS commented 1 year ago

기가막히네요