Skip to content

Instantly share code, notes, and snippets.

@alonecuzzo
Created February 2, 2016 20:14
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alonecuzzo/5bc4e19017b0aba66fe6 to your computer and use it in GitHub Desktop.
Save alonecuzzo/5bc4e19017b0aba66fe6 to your computer and use it in GitHub Desktop.
Thunk Presentation
import UIKit
//POP is great, but there are some gotchas when dealing with protocols that have a Self requirement.
// Self
protocol OwlType {
func hootWithOwlType(owl: Self)
}
class GreatHornedOwl: OwlType {
func hootWithOwlType(owl: GreatHornedOwl) {
// Great Horned Owl Hoot
print("Hoot it out!")
}
}
class BarnOwl: OwlType {
func hootWithOwlType(owl: BarnOwl) {
// Barn Owl Hoot
print("Hooting, Fool, I'm Hooting!")
}
}
// Trait / Mixin - shout out to Wyatt
extension OwlType where Self: GreatHornedOwl {
func hootWithOwlType(owl: GreatHornedOwl) {
// Great Horned Owl Hoot
print("Hoot Fool, Hoot!")
}
}
// AssociatedType
protocol RaptorType {
typealias PreyType//will be changed to associatedType keyword in next Swift version
func hunt() -> [PreyType]
func eat(type: PreyType)
}
protocol Preyable {}
struct Mouse: Preyable {}
struct HummingBird: Preyable {}
struct Vole: Preyable {}
struct GrassHopper: Preyable {}
class EasternScreechOwl: RaptorType {
func hunt() -> [Mouse] {
return [Mouse]()
}
func eat(type: Mouse) {}
}
class GreyOwl: RaptorType {
//removing typealias can slow compile time
func hunt() -> [HummingBird] {
return [HummingBird]()
}
func eat(type: HummingBird) {}
}
//protocols can be used:
//1. parameter or return type in a function
//2. type of a constant, value, property
//3. type of an array or other container
//but generic protocols can only be used as generic constraints!
class Forest<R: RaptorType> {}
func letsGoRaptorWatching<R: RaptorType>(raptor: R) {}
//let theOwl: RaptorType //err
//Swift is an Ahead of Time compiled language. It needs to be able to infer concrete types at compile time. Associated types inside a protocol declaration are abstract. Every type that isn't a function/class needs to be concrete.
let greyOwl: GreatHornedOwl
let easternScreechOwl: EasternScreechOwl
//var someRaptor: RaptorType = random() % 2 == 0 ? greyOwl : easternScreechOwl
//let prey = someRaptor.hunt()
let food: [Preyable] = [GrassHopper(), HummingBird(), Vole(), Mouse()]
//let raptors: [RaptorType] = [greyOwl, easternScreechOwl, easternScreechOwl, greyOwl]
//
//
//for (prey, raptor) in raptors.enumerate() {
// raptor.eat(prey) // what type is prey?? can't be sure
//}
//enter type erasure
//erase abstract in favor of a more concrete type
//a thunk is something that passes
//apple uses it: https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_AnySequence_Structure/index.html#//apple_ref/swift/structctr/AnySequence/s:FVSs11AnySequencecu__Rqd__Ss13GeneratorTypezq_qqd__S0_7Element_FMGS_q__FFT_qd__GS_q__ with AnySequence
// A thunk is a helper struct/class that forwards calls from one object to another object. This is useful for scenarios where those two objects can't normally talk to one another
//I thunk this looks good!
class AnyRaptorType<T>: RaptorType {
private let _hunt: Void -> [T]
private let _eat: T -> Void
required init<U: RaptorType where U.PreyType == T>(_ raptor: U) {
_hunt = raptor.hunt
_eat = raptor.eat
}
func hunt() -> [T] {
return _hunt()
}
func eat(type: T) {
return _eat(type)
}
}
let concreteVoleEatingRaptor: AnyRaptorType<Vole>
//no new diets allowed
let raptorEatingLovely: AnyRaptorType<HummingBird> = AnyRaptorType(GreyOwl())
let raptorOnANewDiet: AnyRaptorType<Mouse> = AnyRaptorType(EasternScreechOwl())
let raptorzThatEatHummingBirds = [AnyRaptorType<HummingBird>]()
let raptorzThatEatVole = [AnyRaptorType<Vole>]()
class AnyPreyable: Preyable {}
class Fox: AnyPreyable {}
class Snake: AnyPreyable {}
class AnyPreyableRaptorType<T: AnyPreyable>: RaptorType {
private let _hunt: Void -> [T]
private let _eat: T -> Void
required init<U: RaptorType where U.PreyType == T>(_ raptor: U) {
_hunt = raptor.hunt
_eat = raptor.eat
}
func hunt() -> [T] {
return _hunt()
}
func eat(type: T) {
return _eat(type)
}
}
class FoxEatingOwl: RaptorType {
func hunt() -> [Fox] {
return [Fox]()
}
func eat(type: Fox) {}
}
class SnakeEatingOwl: RaptorType {
func hunt() -> [Snake] {
return [Snake]()
}
func eat(type: Snake) {}
}
let foxyOwl = AnyPreyableRaptorType(FoxEatingOwl())
let snakeyOwl = AnyPreyableRaptorType(SnakeEatingOwl())
extension AnyPreyableRaptorType {
func preyType() -> T.Type {
return T.self
}
}
let raptorzThatEatPrey = [foxyOwl, foxyOwl]
raptorzThatEatPrey[0] //<Fox>
raptorzThatEatPrey[1] //<Snake>
//raptorzThatEatPrey[1].self.PreyType //how to access the preyType?
let firstRaptor = raptorzThatEatPrey[0]
//fir
let raptorzThatEatFoxes = raptorzThatEatPrey.filter { raptor in
return true
}
//Contravariance & covariance & invariance
//https://en.wikipedia.org/wiki/Liskov_substitution_principle
//The guiding principle behind this is Liskov substitution, which means that an instance of a subclass must be usable anywhere an instance of the superclass can be used.
class Animal {}
class Owl: Animal {}
class Person {
func purchaseAnimal() -> Animal {
return Animal()
}
func pet(animal: Animal) {}
}
//class OwlMan: Person {
// override func purchaseAnimal() -> Owl {
// return Owl()
// }
// override func pet(animal: Owl) {}
//}
//
//
//let person: Person = getAPerson()
//let animal: Animal = getAnAnimal() //what if Dog is returned here!?
//person.pet(animal)
//let rap: AnyPreyableRaptorType<AnyPreyable> //= ... should in theory be equivalent to
//let rap2: AnyPreyableRaptorType<Fox> = rap
let fox = Fox()
let anyPreyable: AnyPreyable = fox
//but DOES NOT WORK because generics are invariant in swift
/**
Covariance is when subtypes are accepted. Overridden read-only properties are covariant.
Contravariance is when supertypes are accepted. The parameters of overridden methods are contravariant.
Invariance is when neither supertypes nor subtypes are accepted. Swift generics are invariant.
Bivariance is when both supertypes and subtypes are accpted. I can't think of any examples of bivariance in Objective-C or Swift.
https://mikeash.com/pyblog/friday-qa-2015-11-20-covariance-and-contravariance.html
**/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment