-
Notifications
You must be signed in to change notification settings - Fork 27
/
DeferredUtils.swift
199 lines (169 loc) · 5.57 KB
/
DeferredUtils.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Deferred
// Haskell, baby.
// Monadic bind/flatMap operator for Deferred.
precedencegroup MonadicBindPrecedence {
associativity: left
higherThan: MonadicDoPrecedence
lowerThan: BitwiseShiftPrecedence
}
precedencegroup MonadicDoPrecedence {
associativity: left
higherThan: MultiplicationPrecedence
}
infix operator >>== : MonadicBindPrecedence
infix operator >>> : MonadicDoPrecedence
@discardableResult public func >>== <T, U>(x: Deferred<Maybe<T>>, f: @escaping (T) -> Deferred<Maybe<U>>) -> Deferred<Maybe<U>> {
return chainDeferred(x, f: f)
}
// A termination case.
public func >>== <T>(x: Deferred<Maybe<T>>, f: @escaping (T) -> Void) {
return x.upon { result in
if let v = result.successValue {
f(v)
}
}
}
// Monadic `do` for Deferred.
@discardableResult public func >>> <T, U>(x: Deferred<Maybe<T>>, f: @escaping () -> Deferred<Maybe<U>>) -> Deferred<Maybe<U>> {
return x.bind { res in
if res.isSuccess {
return f()
}
return deferMaybe(res.failureValue!)
}
}
// Another termination case.
public func >>> <T>(x: Deferred<Maybe<T>>, f: @escaping () -> Void) {
return x.upon { res in
if res.isSuccess {
f()
}
}
}
/**
* Returns a thunk that return a Deferred that resolves to the provided value.
*/
public func always<T>(_ t: T) -> () -> Deferred<Maybe<T>> {
return { deferMaybe(t) }
}
public func deferMaybe<T>(_ s: T) -> Deferred<Maybe<T>> {
return Deferred(value: Maybe(success: s))
}
public func deferMaybe<T>(_ e: MaybeErrorType) -> Deferred<Maybe<T>> {
return Deferred(value: Maybe(failure: e))
}
public typealias Success = Deferred<Maybe<Void>>
@discardableResult public func succeed() -> Success {
return deferMaybe(())
}
/**
* Return a single Deferred that represents the sequential chaining
* of f over the provided items.
*/
public func walk<T>(_ items: [T], f: @escaping (T) -> Success) -> Success {
return items.reduce(succeed()) { success, item -> Success in
success >>> { f(item) }
}
}
/**
* Like `all`, but thanks to its taking thunks as input, each result is
* generated in strict sequence. Fails immediately if any result is failure.
*/
public func accumulate<T>(_ thunks: [() -> Deferred<Maybe<T>>]) -> Deferred<Maybe<[T]>> {
if thunks.isEmpty {
return deferMaybe([])
}
let combined = Deferred<Maybe<[T]>>()
var results: [T] = []
results.reserveCapacity(thunks.count)
var onValue: ((T) -> Void)!
var onResult: ((Maybe<T>) -> Void)!
// onValue and onResult both hold references to each other niling them out before exiting breaks a reference cycle
// We also cannot use unowned here because the thunks are not class types.
onValue = { t in
results.append(t)
if results.count == thunks.count {
onResult = nil
combined.fill(Maybe(success: results))
} else {
thunks[results.count]().upon(onResult)
}
}
onResult = { r in
if r.isFailure {
onValue = nil
combined.fill(Maybe(failure: r.failureValue!))
return
}
onValue(r.successValue!)
}
thunks[0]().upon(onResult)
return combined
}
/**
* Take a function and turn it into a side-effect that can appear
* in a chain of async operations without producing its own value.
*/
public func effect<T, U>(_ f: @escaping (T) -> U) -> (T) -> Deferred<Maybe<T>> {
return { t in
_ = f(t)
return deferMaybe(t)
}
}
/**
* Return a single Deferred that represents the sequential chaining of
* f over the provided items, with the return value chained through.
*/
public func walk<T, U, S: Sequence>(_ items: S, start: Deferred<Maybe<U>>, f: @escaping (T, U) -> Deferred<Maybe<U>>) -> Deferred<Maybe<U>> where S.Iterator.Element == T {
let fs = items.map { item in
return { val in
f(item, val)
}
}
return fs.reduce(start, >>==)
}
/**
* Like `all`, but doesn't accrue individual values.
*/
extension Array where Element: Success {
public func allSucceed() -> Success {
return all(self).bind { results -> Success in
if let failure = results.find({ $0.isFailure }) {
return deferMaybe(failure.failureValue!)
}
return succeed()
}
}
}
public func chainDeferred<T, U>(_ a: Deferred<Maybe<T>>, f: @escaping (T) -> Deferred<Maybe<U>>) -> Deferred<Maybe<U>> {
return a.bind { res in
if let v = res.successValue {
return f(v)
}
return Deferred(value: Maybe<U>(failure: res.failureValue!))
}
}
public func chainResult<T, U>(_ a: Deferred<Maybe<T>>, f: @escaping (T) -> Maybe<U>) -> Deferred<Maybe<U>> {
return a.map { res in
if let v = res.successValue {
return f(v)
}
return Maybe<U>(failure: res.failureValue!)
}
}
public func chain<T, U>(_ a: Deferred<Maybe<T>>, f: @escaping (T) -> U) -> Deferred<Maybe<U>> {
return chainResult(a, f: { Maybe<U>(success: f($0)) })
}
/// Defer-ifies a block to an async dispatch queue.
public func deferDispatchAsync<T>(_ queue: DispatchQueue, f: @escaping () -> Deferred<Maybe<T>>) -> Deferred<Maybe<T>> {
let deferred = Deferred<Maybe<T>>()
queue.async(execute: {
f().upon { result in
deferred.fill(result)
}
})
return deferred
}