1 | # # The Future monad
|
2 | #
|
3 | # The `Future(a, b)` monad represents values that depend on time. This
|
4 | # allows one to model time-based effects explicitly, such that one can
|
5 | # have full knowledge of when they're dealing with delayed computations,
|
6 | # latency, or anything that can not be computed immediately.
|
7 | #
|
8 | # A common use for this monad is to replace the usual
|
9 | # Continuation-Passing Style form of programming, in order to be
|
10 | # able to compose and sequence time-dependent effects using the generic
|
11 | # and powerful monadic operations.
|
12 |
|
13 | /** ^
|
14 | * Copyright (c) 2013 Quildreen Motta
|
15 | *
|
16 | * Permission is hereby granted, free of charge, to any person
|
17 | * obtaining a copy of this software and associated documentation files
|
18 | * (the "Software"), to deal in the Software without restriction,
|
19 | * including without limitation the rights to use, copy, modify, merge,
|
20 | * publish, distribute, sublicense, and/or sell copies of the Software,
|
21 | * and to permit persons to whom the Software is furnished to do so,
|
22 | * subject to the following conditions:
|
23 | *
|
24 | * The above copyright notice and this permission notice shall be
|
25 | * included in all copies or substantial portions of the Software.
|
26 | *
|
27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
28 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
29 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
30 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
31 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
32 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
33 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
34 | */
|
35 |
|
36 |
|
37 | # ## Function: memoised-fork
|
38 | #
|
39 | # A function that memoises the result of a future operation, and updates
|
40 | # the future with the relevant information for the `Show` and `Eq`
|
41 | # typeclass implementations.
|
42 | #
|
43 | # + type: ((a) -> Unit), (b) -> Unit)), Future(a, b) -> ((a) -> Unit, (b) -> Unit)
|
44 | memoised-fork = (f, future) ->
|
45 | pending = []
|
46 | started = false
|
47 | resolved = false
|
48 | rejected = false
|
49 |
|
50 | # The fold applies the correct operation to the future's value, if the
|
51 | # future has been resolved. Or we run the operation instead.
|
52 | #
|
53 | # For optimisation purposes, we cache the result of the operation, so
|
54 | # if we started an operation before, we mark it as started and push
|
55 | # any subsequent forks into a pending queue that will be invoked once
|
56 | # the original fork returns.
|
57 | return fold = (g, h) ->
|
58 | | resolved => h future.value
|
59 | | rejected => g future.value
|
60 | | started => do
|
61 | pending.push rejected: g, resolved: h
|
62 | void
|
63 | | otherwise => do
|
64 | started = true
|
65 | f do
|
66 | * (a) -> do
|
67 | future.is-pending := false
|
68 | future.is-rejected := rejected := true
|
69 | future.value := a
|
70 | invoke-pending \rejected, a
|
71 | g a
|
72 | * (b) -> do
|
73 | future.is-pending := false
|
74 | future.is-resolved := resolved := true
|
75 | future.value := b
|
76 | invoke-pending \resolved, b
|
77 | h b
|
78 |
|
79 | function invoke-pending(kind, value)
|
80 | xs = pending
|
81 | started := false
|
82 | pending.length := 0
|
83 | for x in xs => x[kind] value
|
84 |
|
85 |
|
86 | # ## Function: id
|
87 | #
|
88 | # The identity function.
|
89 | #
|
90 | # + type: a -> a
|
91 | id = (a) -> a
|
92 |
|
93 | # ## Excepction: RejectedFutureExtractionError
|
94 | #
|
95 | # Thrown when trying to extract the value of a rejected future.
|
96 | class RejectedFutureExtractionError extends TypeError
|
97 | -> super "Can't extract the value of a rejected future."
|
98 |
|
99 |
|
100 |
|
101 | # ## Class: Future
|
102 | #
|
103 | # The `Future(a, b)` monad.
|
104 | class Future
|
105 | # ### Section: Constructors ##########################################
|
106 |
|
107 | # #### Constructor
|
108 | #
|
109 | # Creates a new Future for a long-running computation `f`.
|
110 | #
|
111 | # + type: ((a -> Unit), (b -> Unit)) -> Promise(a, b)
|
112 | (f) -> @fork = memoised-fork f, this
|
113 |
|
114 |
|
115 | # ### Section: Predicates ############################################
|
116 |
|
117 | # #### Field: is-pending
|
118 | #
|
119 | # True if the Future hasn't been resolved yet.
|
120 | #
|
121 | # + type: Boolean
|
122 | is-pending: true
|
123 |
|
124 | # #### Field: is-resolved
|
125 | #
|
126 | # True if the Future has been resolved successfully.
|
127 | #
|
128 | # + type: Boolean
|
129 | is-resolved: false
|
130 |
|
131 | # #### Field: is-failure
|
132 | #
|
133 | # True if the Future has been rejected — resolved with a failure.
|
134 | #
|
135 | # + type: Boolean
|
136 | is-rejected: false
|
137 |
|
138 |
|
139 | # ### Section: Applicative ###########################################
|
140 |
|
141 | # #### Function: of
|
142 | #
|
143 | # Constructs a new Future containing the single value `b`.
|
144 | #
|
145 | # `b` can be any value, including `null`, `undefined` or another
|
146 | # `Future(a, b)` monad.
|
147 | #
|
148 | # + type: b -> Future(a, b)
|
149 | @of = (b) -> new Future (reject, resolve) -> resolve b
|
150 | of: (b) -> Future.of b
|
151 |
|
152 | # #### Function: ap
|
153 | #
|
154 | # Applies the function inside the Future to an applicative.
|
155 | #
|
156 | # + type: (@Future(a, b -> c), f:Applicative) => f(b) -> f(c)
|
157 | ap: (b) -> @chain (f) -> b.map f
|
158 |
|
159 |
|
160 | # ### Section: Functor ###############################################
|
161 |
|
162 | # #### Function: map
|
163 | #
|
164 | # Transforms the successful value of the Future using a regular unary
|
165 | # function.
|
166 | #
|
167 | # + type: (@Future(a, b)) => (b -> c) -> Future(a, c)
|
168 | map: (f) -> @chain (a) -> Future.of (f a)
|
169 |
|
170 |
|
171 | # ### Section: Chain #################################################
|
172 |
|
173 | # #### Function: chain
|
174 | #
|
175 | # Transforms the successful value of the Future using a function to a
|
176 | # monad of the same type.
|
177 | #
|
178 | # + type: (@Future(a, b)) => (b -> Future(a, c)) -> Future(a, c)
|
179 | chain: (f) ->
|
180 | new Future (reject, resolve) ~> @fork do
|
181 | * (a) -> reject a
|
182 | * (b) -> (f b).fork reject, resolve
|
183 |
|
184 |
|
185 | # ### Section: Show ##################################################
|
186 |
|
187 | # #### Function: to-string
|
188 | #
|
189 | # Returns a textual representation of the Future monad.
|
190 | #
|
191 | # + type: (@Future(a, b)) => Unit -> String
|
192 | to-string: ->
|
193 | | @is-pending => "Future.Pending"
|
194 | | @is-resolved => "Future.Resolved(#{@value})"
|
195 | | @is-rejected => "Future.Rejected(#{@value})"
|
196 |
|
197 |
|
198 | # ### Section: Eq ####################################################
|
199 |
|
200 | # #### Function: is-equal
|
201 | #
|
202 | # Tests if an Future monad is equal to another Future monad.
|
203 | #
|
204 | # Equality with pending futures is only decidable if the computation
|
205 | # of both futures can be resolved synchronously. Otherwise this
|
206 | # function will return false.
|
207 | #
|
208 | # + type: (@Future(a, b)) => Future(a, b) -> Boolean
|
209 | is-equal: (a) -> Boolean switch
|
210 | | @is-resolved => a.is-resolved and (a.value is @value)
|
211 | | @is-rejected => a.is-rejected and (a.value is @value)
|
212 | | @is-pending => @fork do
|
213 | * (e) -> a.fork do
|
214 | * (e2) -> e is e2
|
215 | * (_) -> false
|
216 | * (s) -> a.fork do
|
217 | * (_) -> false
|
218 | * (s2) -> s is s2
|
219 |
|
220 |
|
221 | # ### Section: Extracting and Recovering #############################
|
222 |
|
223 | # #### Function: or-else
|
224 | #
|
225 | # Transforms a failure value into a new Future monad. Does nothing if
|
226 | # the monad contains a successful value.
|
227 | #
|
228 | # + type: (@Future(a, b)) => (a -> Future(c, b)) -> Future(c, b)
|
229 | or-else: (f) ->
|
230 | new Future (reject, resolve) ~> @fork do
|
231 | * (a) -> (f a).fork reject, resolve
|
232 | * (b) -> resolve b
|
233 |
|
234 | # ### Section: Folds and Extended Transformations ####################
|
235 |
|
236 | # #### Function: fold
|
237 | #
|
238 | # Catamorphism. Takes two functions, applies the leftmost one to the
|
239 | # failure value and the rightmost one to the successful value,
|
240 | # depending on which one is present.
|
241 | #
|
242 | # + type: (@Future(a, b)) => (a -> c) -> (b -> c) -> Future(d, c)
|
243 | fold: (f, g) --> new Future (reject, resolve) ~> @fork do
|
244 | * (a) -> resolve (f a)
|
245 | * (b) -> resolve (g b)
|
246 |
|
247 |
|
248 | # #### Function: swap
|
249 | #
|
250 | # Swaps the disjunction values.
|
251 | #
|
252 | # + type: (@Future(a, b)) => Unit -> Future(b, a)
|
253 | swap: -> new Future (reject, resolve) ~> @fork do
|
254 | * (a) -> resolve a
|
255 | * (b) -> reject b
|
256 |
|
257 | # #### Function: bimap
|
258 | #
|
259 | # Maps both sides of the disjunction.
|
260 | #
|
261 | # + type: (@Future(a, b)) => (a -> c) -> (b -> d) -> Future(c, d)
|
262 | bimap: (f, g) --> new Future (reject, resolve) ~> @fork do
|
263 | * (a) -> reject (f a)
|
264 | * (b) -> resolve (g b)
|
265 |
|
266 | # #### Function: rejected-map
|
267 | #
|
268 | # Maps the left side of the disjunction (failure).
|
269 | #
|
270 | # + type: (@Future(a, b)) => (a -> c) -> Future(c, b)
|
271 | rejected-map: (f) -> new Future (reject, resolve) ~> @fork do
|
272 | * (a) -> reject (f a)
|
273 | * (b) -> resolve b
|
274 |
|
275 |
|
276 | # ## Exports
|
277 | module.exports = Future
|