Last active
February 2, 2023 00:31
-
-
Save unktomi/0c24277e825a1159e7cd568e22bc679f to your computer and use it in GitHub Desktop.
Promises in Swift
This file contains hidden or 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
import Foundation | |
// Somebody at apple should be fired for getting async / await so badly wrong | |
// And presumably not the same person should also get fired for all this crazy @escaping fun | |
func compose<A, B, C>(_ apply: @escaping ((A) -> B), andThen: @escaping (B) -> C) -> ((A) -> C) { | |
return { | |
(x: A) in | |
andThen(apply(x)) | |
} | |
} | |
class Cont<A,R> { | |
var k: (@escaping (A) -> R) -> R | |
init(_ k: @escaping (@escaping (A) -> R) -> R) { | |
self.k = k | |
} | |
func map<B>(_ f: @escaping (A)->B) -> Cont<B,R> { | |
return Map<A,B,R>(k, f) | |
} | |
func flatMap<B>(_ f: @escaping (A) -> Cont<B,R>) -> Cont<B,R> { | |
return Join<A,B,R>(k, f) | |
} | |
func coflatMap<B>(_ f: @escaping (Cont<A,R>) -> B) -> Cont<B,R> { | |
return Unit(f(self)) | |
} | |
func duplicate() -> Cont<Cont<A,R>, R> { | |
return Cont<Cont<A,R>,R>({ | |
resolve in | |
resolve(self) | |
}) | |
} | |
static func resolve<T>(_ x: T) -> Cont<T,Void> { | |
Unit(x) | |
} | |
static func extract<T>(c: Cont<T,T>) -> T { | |
return c.k({ | |
x | |
in x | |
}) | |
} | |
static func callCC<A, R>(f: @escaping ( @escaping (A) -> R) -> R) -> Cont<A,R> { | |
Cont<A,R> { | |
resolve | |
in | |
(f({ | |
x in | |
Cont<A,R> { | |
_ in | |
resolve(x) | |
}.k(resolve) | |
})) | |
} | |
} | |
func isUnit() -> Bool { | |
return false | |
} | |
func isMap() -> Bool { | |
return false | |
} | |
func isJoin() -> Bool { | |
return false | |
} | |
} | |
class Unit<A, R>: Cont<A,R> { | |
init(_ x: A) { | |
super.init({ | |
resolve in | |
resolve(x) | |
}) | |
} | |
override func isUnit() -> Bool { | |
return true | |
} | |
} | |
class Map<A, B, R>: Cont<B,R> { | |
init(_ k: @escaping (@escaping (A)->R)->R,_ f: @escaping (A)->B) { | |
super.init({ | |
(finally: @escaping (B)-> R) in | |
k({ | |
(x: A) in | |
let y = f(x) | |
return finally(y) | |
}) | |
}) | |
} | |
override func isMap() -> Bool { | |
return true | |
} | |
} | |
class Join<A,B,R>: Cont<B,R> { | |
init(_ k: @escaping (@escaping (A)->R)->R,_ f: @escaping (A)->Cont<B,R>) { | |
super.init({ | |
(resolve: @escaping (B)-> R) in | |
k({ | |
(x: A) in | |
let y = f(x) | |
return y.k(resolve) | |
}) | |
}) | |
} | |
override func isJoin() -> Bool { | |
return true | |
} | |
} | |
public class Future<T> { | |
var k: Cont<T, Void> | |
public init(_ value: T) { | |
self.k = Unit(value) | |
} | |
public init(_ addListener: @escaping (@escaping (T) -> Void) -> Void) { | |
self.k = Cont<T, Void>(addListener) | |
} | |
init(_ k: Cont<T, Void>) { | |
self.k = k | |
} | |
public func then<U>(_ f: @escaping (T) -> U) -> Future<U> { | |
return Future<U>(k.map({ | |
(x: T) in | |
let y: U = f(x) | |
return y | |
})) | |
} | |
public func then<U>(_ f: @escaping (T) -> Future<U>) -> Future<U> { | |
let g = { | |
(x: T) in | |
let y: Future<U> = f(x) | |
return y.k | |
} | |
return Future<U>(k.flatMap(g)) | |
} | |
public func run(onCompleted: @escaping (T) -> Void = { _ in }) -> Void { | |
k.k(onCompleted) | |
} | |
public static func resolve<U>(_ value: U) -> Future<U> { | |
return Future<U>(value) | |
} | |
static func resolve<U>(_ k: Cont<U,Void>) -> Future<U> { | |
return Future<U>(k) | |
} | |
public func try_<E>(f: @escaping (T) -> Outcome<T,E>) -> Promise<T,E> { | |
Promise<T,E>(k.map(f)) | |
} | |
} | |
public enum Outcome<R,E> { | |
case resolve(R) | |
case reject(E) | |
} | |
public class Promise<T, E>: Future<Outcome<T,E>> { | |
override init(_ k: Cont<Outcome<T,E>, Void>) { | |
super.init(k) | |
} | |
public init(_ f: @escaping (@escaping (T) -> Void, @escaping (E) -> Void) -> Void) { | |
super.init({ | |
resolve | |
in | |
f({ | |
x in | |
resolve(.resolve(x)) | |
}, | |
{ | |
e in | |
resolve(.reject(e)) | |
}) | |
}) | |
} | |
public func catch_ (handler: @escaping (E) -> T) -> Future<T> { | |
Future<T>( | |
k.map({ | |
x in | |
switch (x) { | |
case .reject(let e): | |
return handler(e) | |
case .resolve(let t): | |
return t | |
} | |
}) | |
) | |
} | |
public func catch_ (handler: @escaping (E) -> Future<T>) -> Future<T> { | |
Future<T>( | |
k.flatMap({ | |
x in | |
switch (x) { | |
case .reject(let e): | |
return handler(e).k | |
case .resolve(let x): | |
return .resolve(x) | |
} | |
}) | |
) | |
} | |
} | |
public func first<T>(_ f: @escaping () -> T) -> Future<T> { | |
return Future<Void>(()).then(f) | |
} | |
public func first<T>(_ f: @escaping () -> Future<T>) -> Future<T> { | |
return Future<Void>(()).then(f) | |
} | |
func test() { | |
var x = 3 | |
first { | |
delay(seconds: 2.0) | |
}.then { | |
x = 4 | |
}.then { | |
let y = 5 | |
first { | |
print(x + y) | |
}.then { | |
delay(seconds: 3.0) | |
} | |
.then { | |
99 | |
}.try_ { | |
n in | |
if (n < 100) { | |
return .reject("fail") | |
} | |
return .resolve(n) | |
}.catch_ { | |
(err: String) in | |
print(err) | |
return delay(seconds: 3.0) | |
.then { | |
100 | |
} | |
}.then { | |
(n) in | |
print(x + y + n) | |
} | |
}.run() | |
} | |
public func delay(seconds: Double) -> Future<Void> { | |
return Future({ | |
(resolve) in | |
let g = { | |
resolve(()) | |
} | |
DispatchQueue.main.asyncAfter(deadline: .now() + seconds, execute: g) | |
} | |
) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment