forked from mozilla-mobile/firefox-ios
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDeferredUtils.swift
183 lines (156 loc) · 4.87 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
/* 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.
infix operator >>== { associativity left precedence 160 }
public func >>== <T, U>(x: Deferred<Maybe<T>>, f: T -> Deferred<Maybe<U>>) -> Deferred<Maybe<U>> {
return chainDeferred(x, f: f)
}
// A termination case.
public func >>== <T>(x: Deferred<Maybe<T>>, f: T -> ()) {
return x.upon { result in
if let v = result.successValue {
f(v)
}
}
}
// Monadic `do` for Deferred.
infix operator >>> { associativity left precedence 150 }
public func >>> <T, U>(x: Deferred<Maybe<T>>, f: () -> 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: () -> ()) {
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<()>>
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: 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 -> ())!
var onResult: (Maybe<T> -> ())!
onValue = { t in
results.append(t)
if results.count == thunks.count {
combined.fill(Maybe(success: results))
} else {
thunks[results.count]().upon(onResult)
}
}
onResult = { r in
if r.isFailure {
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: 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>(items: [T], start: Deferred<Maybe<U>>, f: (T, U) -> Deferred<Maybe<U>>) -> Deferred<Maybe<U>> {
let fs = items.map { item in
return { val in
f(item, val)
}
}
return fs.reduce(start, combine: >>==)
}
/**
* 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: 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: 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: 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: dispatch_queue_t, f: () -> Deferred<Maybe<T>>) -> Deferred<Maybe<T>> {
let deferred = Deferred<Maybe<T>>()
dispatch_async(queue, {
f().upon { result in
deferred.fill(result)
}
})
return deferred
}