Last active
June 6, 2016 22:47
-
-
Save therealbnut/19a681b5ed2be4a9234d177e6c158d0b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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