Skip to content

Instantly share code, notes, and snippets.

@therealbnut
Last active June 6, 2016 22:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save therealbnut/19a681b5ed2be4a9234d177e6c158d0b to your computer and use it in GitHub Desktop.
Save therealbnut/19a681b5ed2be4a9234d177e6c158d0b to your computer and use it in GitHub Desktop.
// Output:
// AnyPokemon<πŸ”₯>(Ponyta())
// AnyPokemon<πŸ”₯>(Charmander())
protocol Pokemon {
associatedtype Power
mutating func attack() -> Power
}
struct Pikachu: Pokemon {
func attack() -> 🌩 {
return 🌩()
}
}
struct Charmander: Pokemon {
func attack() -> πŸ”₯ {
return πŸ”₯()
}
}
struct Ponyta: Pokemon {
func attack() -> πŸ”₯ {
return πŸ”₯()
}
}
// power types
struct πŸ”₯ {}
struct 🌩 {}
// MARK: - Type Erasure
/*
We need a generic base that can represent any pokemon with a specific power.
This class is private so we can use value-semantics still.
We could use it instead of 'struct AnyPokemon'.
We use fatalError because we don't have enough information to implement yet.
*/
private class _AnyPokemonWithPower<Power>: NonObjectiveCBase, Pokemon, CustomStringConvertible {
func attack() -> Power { fatalError() }
var description: String { fatalError() }
func clone() -> _AnyPokemonWithPower { fatalError() }
}
/*
This Subclass of _AnyPokemon allows us to use the implementation from a
specific Pokemon.
*/
private final class _AnyPokemonOfType<P: Pokemon>: _AnyPokemonWithPower<P.Power> {
private var pokemon: P
init(_ pokemon: P) {
self.pokemon = pokemon
}
override func attack() -> P.Power {
return self.pokemon.attack()
}
override var description: String {
return "AnyPokemon<\(P.Power.self)>(\(self.pokemon))"
}
override func clone() -> _AnyPokemonWithPower<P.Power> {
return _AnyPokemonOfType(pokemon)
}
}
/*
This is the actual AnyPokemon struct, it uses _AnyPokemonOfType to
get an _AnyPokemonWithPower with the appropriate Power.
*/
struct AnyPokemon<Power>: Pokemon, CustomStringConvertible {
private var base: _AnyPokemonWithPower<Power>
init<P: Pokemon where P.Power == Power>(_ pokemon: P) {
self.base = _AnyPokemonOfType(pokemon)
}
private mutating func copyOnWrite<T>(
@noescape apply: _AnyPokemonWithPower<Power> -> T
) -> T {
if !isUniquelyReferenced(&self.base) {
self.base = self.base.clone()
}
return apply(self.base)
}
mutating func attack() -> Power {
return self.copyOnWrite { base in
base.attack()
}
}
var description: String {
return self.base.description
}
}
// MARK: - now we can use it!
let pokemon: [AnyPokemon<πŸ”₯>] = [
AnyPokemon(Ponyta()),
AnyPokemon(Charmander())
]
pokemon.forEach { 🐣 in
print("\(🐣)")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment