CommodityType /// The upper bound existential type for the associated type does not safety convert to the actual concrete type, because the concrete type is unknown. func eat(_: some FeedType) } //struct Chicken: Animal { // // func produce() -> Egg { // return Egg() // } // // func eat(_: FeedType) { // // } //} struct Cow: Animal { func produce() -> Milk { return Milk() } func eat(_: Hay) {} } protocol Food { } struct Egg: Food { } struct Milk: Food { } struct Farm { /// This strategy of using the same representation for different concrete types is called type erasure. var animals: [any Animal] func produceCommodities() -> [any Food] { return animals.map { animal in animal.produce() } } } protocol AnimalFeed { } struct "> CommodityType /// The upper bound existential type for the associated type does not safety convert to the actual concrete type, because the concrete type is unknown. func eat(_: some FeedType) } //struct Chicken: Animal { // // func produce() -> Egg { // return Egg() // } // // func eat(_: FeedType) { // // } //} struct Cow: Animal { func produce() -> Milk { return Milk() } func eat(_: Hay) {} } protocol Food { } struct Egg: Food { } struct Milk: Food { } struct Farm { /// This strategy of using the same representation for different concrete types is called type erasure. var animals: [any Animal] func produceCommodities() -> [any Food] { return animals.map { animal in animal.produce() } } } protocol AnimalFeed { } struct "> CommodityType /// The upper bound existential type for the associated type does not safety convert to the actual concrete type, because the concrete type is unknown. func eat(_: some FeedType) } //struct Chicken: Animal { // // func produce() -> Egg { // return Egg() // } // // func eat(_: FeedType) { // // } //} struct Cow: Animal { func produce() -> Milk { return Milk() } func eat(_: Hay) {} } protocol Food { } struct Egg: Food { } struct Milk: Food { } struct Farm { /// This strategy of using the same representation for different concrete types is called type erasure. var animals: [any Animal] func produceCommodities() -> [any Food] { return animals.map { animal in animal.produce() } } } protocol AnimalFeed { } struct ">
import UIKit

var greeting = "Hello, playground"

protocol Animal {
  associatedtype CommodityType: Food
//  associatedtype CommodityType
  associatedtype FeedType: AnimalFeed

  func produce() -> CommodityType
  
  /// The upper bound existential type for the associated type does not safety convert to the actual concrete type, because the concrete type is unknown.
  func eat(_: some FeedType)
}

//struct Chicken: Animal {
//
//  func produce() -> Egg {
//    return Egg()
//  }
//
//  func eat(_: FeedType) {
//
//  }
//}

struct Cow: Animal {
  
  func produce() -> Milk {
    return Milk()
  }
  
  func eat(_: Hay) {}
}

protocol Food {
  
}

struct Egg: Food {
  
}

struct Milk: Food {
  
}

struct Farm {
  /// This strategy of using the same representation for different concrete types is called type erasure.
  var animals: [any Animal]
  
  func produceCommodities() -> [any Food] {
    return animals.map { animal in
      animal.produce()
    }
  }
}

protocol AnimalFeed { }

struct Hay: AnimalFeed { }

let animals: [any Animal] = [Cow()]

/// Type erasure does not allow us to work with associated types in consuming position.

animals.map { animal in
  animal.eat(<#T##Animal.FeedType#>)
}