-
-
Save bkobilansky/8d8b8a36b6403d9ea6986f69b933b253 to your computer and use it in GitHub Desktop.
Type erasure with multiple adopting types
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
// Paste me into a playground! | |
import Cocoa | |
//: # Basic Setup | |
protocol FancyProtocol { | |
associatedtype Thing | |
func holdPinkyUp(x: Thing) | |
} | |
struct Dashing: FancyProtocol { | |
func holdPinkyUp(x: String) { print("Dashing: \(x)") } | |
} | |
struct Spiffy: FancyProtocol { | |
func holdPinkyUp(x: String) { print("Spiffy: \(x)") } | |
} | |
//: ## BoxBase | |
//: The base implements the protocol but everything just fatal errors. | |
//: It exists to give us an abstraction we can override later | |
class AnyFancyBoxBase<T>: FancyProtocol { | |
func holdPinkyUp(x: T) { | |
//never called | |
fatalError() | |
} | |
} | |
//: ## The Box | |
//: Here we override the BoxBase and specify that our generic parameter | |
//: implements the protocol. This is just a trampoline that forwards | |
//: everything to base. | |
final class _FancyBox<Base: FancyProtocol>: AnyFancyBoxBase<Base.Thing> { | |
var base: Base | |
init(_ base: Base) { | |
self.base = base | |
} | |
override func holdPinkyUp(x: Base.Thing) { | |
base.holdPinkyUp(x: x) | |
} | |
} | |
//: ## Type-erased wrapper | |
//: Our type-erased AnyFancy that specifies the base box class, | |
//: which being a base class frees us from knowing what is inside it. | |
//: | |
//: The generic initializer is using FancyProtocol as a generic constraint | |
//: and thus we satisfy the requirement of using an associated type | |
struct AnyFancy<T>: FancyProtocol { | |
var _box: AnyFancyBoxBase<T> | |
func holdPinkyUp(x: T) { | |
_box.holdPinkyUp(x: x) | |
} | |
init<U: FancyProtocol>(_ base: U) where U.Thing == T { | |
_box = _FancyBox(base) | |
} | |
} | |
let dashing = Dashing() | |
let spiffy = Spiffy() | |
//: ## Magic | |
//: Our type-erased AnyFancy that specifies the base box class, | |
//: which being a base class frees us from caring about the implementation | |
var anyFancy = AnyFancy(dashing) | |
print("\(type(of: anyFancy))") | |
anyFancy.holdPinkyUp(x: "ok") | |
//: Because Spiffy binds FancyProtocol.Thing to String it is compatible | |
anyFancy = AnyFancy(spiffy) | |
anyFancy.holdPinkyUp(x: "woo") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment