Skip to content

Instantly share code, notes, and snippets.

@pteasima
Last active January 7, 2018 20:43
Show Gist options
  • Save pteasima/336dafec2f01ec9f0f7cd3c2656f7436 to your computer and use it in GitHub Desktop.
Save pteasima/336dafec2f01ec9f0f7cd3c2656f7436 to your computer and use it in GitHub Desktop.
tagless-map-problem
protocol Effect {
associatedtype Action
static var none: Self { get }
static func batch(_ effects: [Self]) -> Self
// ! What should we return? Effect has associated types. Effect<B>, Self<B> are not possible.
// Feels like a usecase for type-erasure but didnt get me anywhere (+ I feel like a type-erased AnyEffect defeats the purpose of tagless)
//func map<B>(_ transform: @escaping (Action) -> B) -> ???
// this works but is too general, we want to keep OtherEffect fixed to the same implementation as Self
func map<OtherEffect>(_ transform: @escaping (Action) -> OtherEffect.Action) -> OtherEffect where OtherEffect: Effect
}
struct Execute<A> {
let run: (_ callback: @escaping (A) -> ()) -> ()
}
extension Execute: Effect {
typealias Action = A
static var none: Execute<A> {
return Execute { _ in () }
}
static func batch(_ effects: [Execute<A>]) -> Execute<A> {
return Execute { callback in
for c in effects {
c.run(callback)
}
}
}
// ugly runtime typechecking. Lucking we have .none so we dont have to trap, but I still dont like this
func map<OtherEffect>(_ transform: @escaping (A) -> OtherEffect.Action) -> OtherEffect where OtherEffect : Effect {
guard OtherEffect.self == Execute<OtherEffect.Action>.self else {
assertionFailure()
return .none
}
return Execute<OtherEffect.Action> { callback in
self.run { callback(transform($0)) }
} as! OtherEffect
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment