Skip to content

Instantly share code, notes, and snippets.

@unktomi
Last active February 2, 2023 00:31
Show Gist options
  • Save unktomi/0c24277e825a1159e7cd568e22bc679f to your computer and use it in GitHub Desktop.
Save unktomi/0c24277e825a1159e7cd568e22bc679f to your computer and use it in GitHub Desktop.
Promises in Swift
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