Skip to content

Instantly share code, notes, and snippets.

@avaidyam
Created January 29, 2018 04:10
Show Gist options
  • Save avaidyam/ab1dbf4fda5107b2666aa7a662f528ca to your computer and use it in GitHub Desktop.
Save avaidyam/ab1dbf4fda5107b2666aa7a662f528ca to your computer and use it in GitHub Desktop.
Swift (naive) partial application for async functions
import Dispatch
/// Manages async state for a function from its exterior.
fileprivate final class AsyncSlot<T> {
private let semaphore = DispatchSemaphore(value: 0)
private var value: T! = nil
fileprivate func fill(_ value: T) {
self.value = value
self.semaphore.signal()
}
fileprivate func retrieve() -> T {
self.semaphore.wait()
return self.value
}
fileprivate static func perform<X>(_ handler: (AsyncSlot<X>) -> ()) -> X {
let slot = AsyncSlot<X>()
handler(slot)
return slot.retrieve()
}
}
public typealias SyncUnit0_0 = (() -> ())
public typealias AsyncUnit0_0 = (((@escaping () -> Void)) -> Void)
public func await(_ f: @escaping AsyncUnit0_0) -> SyncUnit0_0 {
return { AsyncSlot<()>.perform { slot in (f() { slot.fill(()) }) } }
}
public typealias SyncUnit1_0<A> = ((A) -> ())
public typealias AsyncUnit1_0<A> = ((A, (@escaping () -> Void)) -> Void)
public func await<A>(_ f: @escaping AsyncUnit1_0<A>) -> SyncUnit1_0<A> {
return { a in AsyncSlot<()>.perform { slot in (f(a) { slot.fill(()) }) } }
}
public typealias SyncUnit2_0<A, B> = ((A, B) -> ())
public typealias AsyncUnit2_0<A, B> = ((A, B, (@escaping () -> Void)) -> Void)
public func await<A, B>(_ f: @escaping AsyncUnit2_0<A, B>) -> SyncUnit2_0<A, B> {
return { a, b in AsyncSlot<()>.perform { slot in (f(a, b) { slot.fill(()) }) } }
}
public typealias SyncUnit3_0<A, B, C> = ((A, B, C) -> ())
public typealias AsyncUnit3_0<A, B, C> = ((A, B, C, (@escaping () -> Void)) -> Void)
public func await<A, B, C>(_ f: @escaping AsyncUnit3_0<A, B, C>) -> SyncUnit3_0<A, B, C> {
return { a, b, c in AsyncSlot<()>.perform { slot in (f(a, b, c) { slot.fill(()) }) } }
}
public typealias SyncUnit0_1<X> = (() -> (X))
public typealias AsyncUnit0_1<X> = (((@escaping (X) -> Void)) -> Void)
public func await<X>(_ f: @escaping AsyncUnit0_1<X>) -> SyncUnit0_1<X> {
return { AsyncSlot<(X)>.perform { slot in (f() { x in slot.fill((x)) }) } }
}
public typealias SyncUnit1_1<A, X> = ((A) -> (X))
public typealias AsyncUnit1_1<A, X> = ((A, (@escaping (X) -> Void)) -> Void)
public func await<A, X>(_ f: @escaping AsyncUnit1_1<A, X>) -> SyncUnit1_1<A, X> {
return { a in AsyncSlot<(X)>.perform { slot in (f(a) { x in slot.fill((x)) }) } }
}
public typealias SyncUnit2_1<A, B, X> = ((A, B) -> (X))
public typealias AsyncUnit2_1<A, B, X> = ((A, B, (@escaping (X) -> Void)) -> Void)
public func await<A, B, X>(_ f: @escaping AsyncUnit2_1<A, B, X>) -> SyncUnit2_1<A, B, X> {
return { a, b in AsyncSlot<(X)>.perform { slot in (f(a, b) { x in slot.fill((x)) }) } }
}
public typealias SyncUnit3_1<A, B, C, X> = ((A, B, C) -> (X))
public typealias AsyncUnit3_1<A, B, C, X> = ((A, B, C, (@escaping (X) -> Void)) -> Void)
public func await<A, B, C, X>(_ f: @escaping AsyncUnit3_1<A, B, C, X>) -> SyncUnit3_1<A, B, C, X> {
return { a, b, c in AsyncSlot<(X)>.perform { slot in (f(a, b, c) { x in slot.fill((x)) }) } }
}
public typealias SyncUnit0_2<X, Y> = (() -> (X, Y))
public typealias AsyncUnit0_2<X, Y> = (((@escaping (X, Y) -> Void)) -> Void)
public func await<X, Y>(_ f: @escaping AsyncUnit0_2<X, Y>) -> SyncUnit0_2<X, Y> {
return { AsyncSlot<(X, Y)>.perform { slot in (f() { x, y in slot.fill((x, y)) }) } }
}
public typealias SyncUnit1_2<A, X, Y> = ((A) -> (X, Y))
public typealias AsyncUnit1_2<A, X, Y> = ((A, (@escaping (X, Y) -> Void)) -> Void)
public func await<A, X, Y>(_ f: @escaping AsyncUnit1_2<A, X, Y>) -> SyncUnit1_2<A, X, Y> {
return { a in AsyncSlot<(X, Y)>.perform { slot in (f(a) { x, y in slot.fill((x, y)) }) } }
}
public typealias SyncUnit2_2<A, B, X, Y> = ((A, B) -> (X, Y))
public typealias AsyncUnit2_2<A, B, X, Y> = ((A, B, (@escaping (X, Y) -> Void)) -> Void)
public func await<A, B, X, Y>(_ f: @escaping AsyncUnit2_2<A, B, X, Y>) -> SyncUnit2_2<A, B, X, Y> {
return { a, b in AsyncSlot<(X, Y)>.perform { slot in (f(a, b) { x, y in slot.fill((x, y)) }) } }
}
public typealias SyncUnit3_2<A, B, C, X, Y> = ((A, B, C) -> (X, Y))
public typealias AsyncUnit3_2<A, B, C, X, Y> = ((A, B, C, (@escaping (X, Y) -> Void)) -> Void)
public func await<A, B, C, X, Y>(_ f: @escaping AsyncUnit3_2<A, B, C, X, Y>) -> SyncUnit3_2<A, B, C, X, Y> {
return { a, b, c in AsyncSlot<(X, Y)>.perform { slot in (f(a, b, c) { x, y in slot.fill((x, y)) }) } }
}
public typealias SyncUnit0_3<X, Y, Z> = (() -> (X, Y, Z))
public typealias AsyncUnit0_3<X, Y, Z> = (((@escaping (X, Y, Z) -> Void)) -> Void)
public func await<X, Y, Z>(_ f: @escaping AsyncUnit0_3<X, Y, Z>) -> SyncUnit0_3<X, Y, Z> {
return { AsyncSlot<(X, Y, Z)>.perform { slot in (f() { x, y, z in slot.fill((x, y, z)) }) } }
}
public typealias SyncUnit1_3<A, X, Y, Z> = ((A) -> (X, Y, Z))
public typealias AsyncUnit1_3<A, X, Y, Z> = ((A, (@escaping (X, Y, Z) -> Void)) -> Void)
public func await<A, X, Y, Z>(_ f: @escaping AsyncUnit1_3<A, X, Y, Z>) -> SyncUnit1_3<A, X, Y, Z> {
return { a in AsyncSlot<(X, Y, Z)>.perform { slot in (f(a) { x, y, z in slot.fill((x, y, z)) }) } }
}
public typealias SyncUnit2_3<A, B, X, Y, Z> = ((A, B) -> (X, Y, Z))
public typealias AsyncUnit2_3<A, B, X, Y, Z> = ((A, B, (@escaping (X, Y, Z) -> Void)) -> Void)
public func await<A, B, X, Y, Z>(_ f: @escaping AsyncUnit2_3<A, B, X, Y, Z>) -> SyncUnit2_3<A, B, X, Y, Z> {
return { a, b in AsyncSlot<(X, Y, Z)>.perform { slot in (f(a, b) { x, y, z in slot.fill((x, y, z)) }) } }
}
public typealias SyncUnit3_3<A, B, C, X, Y, Z> = ((A, B, C) -> (X, Y, Z))
public typealias AsyncUnit3_3<A, B, C, X, Y, Z> = ((A, B, C, (@escaping (X, Y, Z) -> Void)) -> Void)
public func await<A, B, C, X, Y, Z>(_ f: @escaping AsyncUnit3_3<A, B, C, X, Y, Z>) -> SyncUnit3_3<A, B, C, X, Y, Z> {
return { a, b, c in AsyncSlot<(X, Y, Z)>.perform { slot in (f(a, b, c) { x, y, z in slot.fill((x, y, z)) }) } }
}
import Dispatch
public func test(a: Int, b: Int, _ handler: @escaping (Int?) -> ()) {
DispatchQueue.global(qos: .background).async {
handler(a + b)
}
}
test(a: 1, b: 1) { print("global", $0) }
print("main", await(test(a:b:_:))(1, 1))
dispatchMain()
@avaidyam
Copy link
Author

Realistically, you don't even need AsyncSlot<T> - just a local tuple (DispatchSemaphore, T). The current implementation of AsyncSlot<T> can be an issue because it expects that it will first be retrieve()'ed and then fill()'ed, but can't handle anything outside of that exact order. In addition, it creates the semaphore upon init, where it may not be needed at all if the order of operations is reversed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment