1 | // Copyright (c) 2013-2014 Quildreen Motta <quildreen@gmail.com>
|
2 | //
|
3 | // Permission is hereby granted, free of charge, to any person
|
4 | // obtaining a copy of this software and associated documentation files
|
5 | // (the "Software"), to deal in the Software without restriction,
|
6 | // including without limitation the rights to use, copy, modify, merge,
|
7 | // publish, distribute, sublicense, and/or sell copies of the Software,
|
8 | // and to permit persons to whom the Software is furnished to do so,
|
9 | // subject to the following conditions:
|
10 | //
|
11 | // The above copyright notice and this permission notice shall be
|
12 | // included in all copies or substantial portions of the Software.
|
13 | //
|
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21 |
|
22 | /**
|
23 | * @module lib/maybe
|
24 | */
|
25 | module.exports = Maybe
|
26 |
|
27 | // -- Aliases ----------------------------------------------------------
|
28 | var clone = Object.create
|
29 | var unimplemented = function(){ throw new Error('Not implemented.') }
|
30 | var noop = function(){ return this }
|
31 |
|
32 | // -- Implementation ---------------------------------------------------
|
33 |
|
34 | /**
|
35 | * A structure for values that may not be present, or computations that may
|
36 | * fail. `Maybe(a)` explicitly models the effects that are implicit in
|
37 | * `Nullable` types, thus has none of the problems associated with
|
38 | * `null` or `undefined` — like `NullPointerExceptions`.
|
39 | *
|
40 | * The class models two different cases:
|
41 | *
|
42 | * + `Just a` — represents a `Maybe(a)` that contains a value. `a` may
|
43 | * be any value, including `null` or `undefined`.
|
44 | *
|
45 | * + `Nothing` — represents a `Maybe(a)` that has no values. Or a
|
46 | * failure that needs no additional information.
|
47 | *
|
48 | * Common uses of this structure includes modelling values that may or may
|
49 | * not be present in a collection, thus instead of needing a
|
50 | * `collection.has(a)`, the `collection.get(a)` operation gives you all
|
51 | * the information you need — `collection.get(a).is-nothing` being
|
52 | * equivalent to `collection.has(a)`; Similarly the same reasoning may
|
53 | * be applied to computations that may fail to provide a value, e.g.:
|
54 | * `collection.find(predicate)` can safely return a `Maybe(a)` instance,
|
55 | * even if the collection contains nullable values.
|
56 | *
|
57 | * Furthermore, the values of `Maybe(a)` can be combined and manipulated
|
58 | * by using the expressive monadic operations. This allows safely
|
59 | * sequencing operations that may fail, and safely composing values that
|
60 | * you don't know whether they're present or not, failing early
|
61 | * (returning a `Nothing`) if any of the operations fail.
|
62 | *
|
63 | * If one wants to store additional information about failures, the
|
64 | * [Either][] and [Validation][] structures provide such a capability, and
|
65 | * should be used instead of the `Maybe(a)` structure.
|
66 | *
|
67 | * [Either]: https://github.com/folktale/data.either
|
68 | * [Validation]: https://github.com/folktale/data.validation
|
69 | *
|
70 | *
|
71 | * @class
|
72 | */
|
73 | function Maybe() {}
|
74 |
|
75 | // The case for successful values
|
76 | Just.prototype = clone(Maybe.prototype)
|
77 | function Just(a){
|
78 | this.value = a
|
79 | }
|
80 |
|
81 | // The case for failure values
|
82 | Nothing.prototype = clone(Maybe.prototype)
|
83 | function Nothing(){}
|
84 |
|
85 |
|
86 | // -- Constructors -----------------------------------------------------
|
87 |
|
88 | /**
|
89 | * Constructs a new `Maybe[α]` structure with an absent value. Commonly used
|
90 | * to represent a failure.
|
91 | *
|
92 | * @summary Void → Maybe[α]
|
93 | */
|
94 | Maybe.Nothing = function() {
|
95 | return new Nothing
|
96 | }
|
97 | Maybe.prototype.Nothing = Maybe.Nothing
|
98 |
|
99 | /**
|
100 | * Constructs a new `Maybe[α]` structure that holds the single value
|
101 | * `α`. Commonly used to represent a success.
|
102 | *
|
103 | * `α` can be any value, including `null`, `undefined` or another
|
104 | * `Maybe[α]` structure.
|
105 | *
|
106 | * @summary α → Maybe[α]
|
107 | */
|
108 | Maybe.Just = function(a) {
|
109 | return new Just(a)
|
110 | }
|
111 | Maybe.prototype.Just = Maybe.Just
|
112 |
|
113 |
|
114 | // -- Conversions ------------------------------------------------------
|
115 |
|
116 | /**
|
117 | * Constructs a new `Maybe[α]` structure from a nullable type.
|
118 | *
|
119 | * If the value is either `null` or `undefined`, this function returns a
|
120 | * `Nothing`, otherwise the value is wrapped in a `Just(α)`.
|
121 | *
|
122 | * @summary α → Maybe[α]
|
123 | */
|
124 | Maybe.fromNullable = function(a) {
|
125 | return a != null? new Just(a)
|
126 | : /* otherwise */ new Nothing
|
127 | }
|
128 | Maybe.prototype.fromNullable = Maybe.fromNullable
|
129 |
|
130 | /**
|
131 | * Constructs a new `Maybe[β]` structure from an `Either[α, β]` type.
|
132 | *
|
133 | * The left side of the `Either` becomes `Nothing`, and the right side
|
134 | * is wrapped in a `Just(β)`.
|
135 | *
|
136 | * @summary Either[α, β] → Maybe[β]
|
137 | */
|
138 | Maybe.fromEither = function(a) {
|
139 | return a.fold(Maybe.Nothing, Maybe.Just)
|
140 | }
|
141 | Maybe.prototype.fromEither = Maybe.fromEither
|
142 |
|
143 | /**
|
144 | * Constructs a new `Maybe[β]` structure from a `Validation[α, β]` type.
|
145 | *
|
146 | * The failure side of the `Validation` becomes `Nothing`, and the right
|
147 | * side is wrapped in a `Just(β)`.
|
148 | *
|
149 | * @method
|
150 | * @summary Validation[α, β] → Maybe[β]
|
151 | */
|
152 | Maybe.fromValidation = Maybe.fromEither
|
153 | Maybe.prototype.fromValidation = Maybe.fromEither
|
154 |
|
155 |
|
156 | // -- Predicates -------------------------------------------------------
|
157 |
|
158 | /**
|
159 | * True if the `Maybe[α]` structure contains a failure (i.e.: `Nothing`).
|
160 | *
|
161 | * @summary Boolean
|
162 | */
|
163 | Maybe.prototype.isNothing = false
|
164 | Nothing.prototype.isNothing = true
|
165 |
|
166 |
|
167 | /**
|
168 | * True if the `Maybe[α]` structure contains a single value (i.e.: `Just(α)`).
|
169 | *
|
170 | * @summary Boolean
|
171 | */
|
172 | Maybe.prototype.isJust = false
|
173 | Just.prototype.isJust = true
|
174 |
|
175 |
|
176 | // -- Applicative ------------------------------------------------------
|
177 |
|
178 | /**
|
179 | * Creates a new `Maybe[α]` structure holding the single value `α`.
|
180 | *
|
181 | * `α` can be any value, including `null`, `undefined`, or another
|
182 | * `Maybe[α]` structure.
|
183 | *
|
184 | * @summary α → Maybe[α]
|
185 | */
|
186 | Maybe.of = function(a) {
|
187 | return Maybe.prototype.Just(a)
|
188 | }
|
189 | Maybe.prototype.of = Maybe.of
|
190 |
|
191 |
|
192 | /**
|
193 | * Applies the function inside the `Maybe[α]` structure to another
|
194 | * applicative type.
|
195 | *
|
196 | * The `Maybe[α]` structure should contain a function value, otherwise a
|
197 | * `TypeError` is thrown.
|
198 | *
|
199 | * @method
|
200 | * @summary (@Maybe[α → β], f:Applicative[_]) => f[α] → f[β]
|
201 | */
|
202 | Maybe.prototype.ap = unimplemented
|
203 |
|
204 | Nothing.prototype.ap = noop
|
205 |
|
206 | Just.prototype.ap = function(b) {
|
207 | return b.map(this.value)
|
208 | }
|
209 |
|
210 |
|
211 |
|
212 |
|
213 | // -- Functor ----------------------------------------------------------
|
214 |
|
215 | /**
|
216 | * Transforms the value of the `Maybe[α]` structure using a regular unary
|
217 | * function.
|
218 | *
|
219 | * @method
|
220 | * @summary @Maybe[α] => (α → β) → Maybe[β]
|
221 | */
|
222 | Maybe.prototype.map = unimplemented
|
223 | Nothing.prototype.map = noop
|
224 |
|
225 | Just.prototype.map = function(f) {
|
226 | return this.of(f(this.value))
|
227 | }
|
228 |
|
229 |
|
230 | // -- Chain ------------------------------------------------------------
|
231 |
|
232 | /**
|
233 | * Transforms the value of the `Maybe[α]` structure using an unary function
|
234 | * to monads.
|
235 | *
|
236 | * @method
|
237 | * @summary (@Maybe[α], m:Monad[_]) => (α → m[β]) → m[β]
|
238 | */
|
239 | Maybe.prototype.chain = unimplemented
|
240 | Nothing.prototype.chain = noop
|
241 |
|
242 | Just.prototype.chain = function(f) {
|
243 | return f(this.value)
|
244 | }
|
245 |
|
246 |
|
247 | // -- Show -------------------------------------------------------------
|
248 |
|
249 | /**
|
250 | * Returns a textual representation of the `Maybe[α]` structure.
|
251 | *
|
252 | * @method
|
253 | * @summary @Maybe[α] => Void → String
|
254 | */
|
255 | Maybe.prototype.toString = unimplemented
|
256 |
|
257 | Nothing.prototype.toString = function() {
|
258 | return 'Maybe.Nothing'
|
259 | }
|
260 |
|
261 | Just.prototype.toString = function() {
|
262 | return 'Maybe.Just(' + this.value + ')'
|
263 | }
|
264 |
|
265 |
|
266 | // -- Eq ---------------------------------------------------------------
|
267 |
|
268 | /**
|
269 | * Tests if a `Maybe[α]` structure is equal to another `Maybe[α]` structure.
|
270 | *
|
271 | * @method
|
272 | * @summary @Maybe[α] => Maybe[α] → Boolean
|
273 | */
|
274 | Maybe.prototype.isEqual = unimplemented
|
275 |
|
276 | Nothing.prototype.isEqual = function(b) {
|
277 | return b.isNothing
|
278 | }
|
279 |
|
280 | Just.prototype.isEqual = function(b) {
|
281 | return b.isJust
|
282 | && b.value === this.value
|
283 | }
|
284 |
|
285 |
|
286 | // -- Extracting and recovering ----------------------------------------
|
287 |
|
288 | /**
|
289 | * Extracts the value out of the `Maybe[α]` structure, if it
|
290 | * exists. Otherwise throws a `TypeError`.
|
291 | *
|
292 | * @method
|
293 | * @summary @Maybe[α] => Void → a, :: partial, throws
|
294 | * @see {@link module:lib/maybe~Maybe#getOrElse} — A getter that can handle failures
|
295 | * @throws {TypeError} if the structure has no value (`Nothing`).
|
296 | */
|
297 | Maybe.prototype.get = unimplemented
|
298 |
|
299 | Nothing.prototype.get = function() {
|
300 | throw new TypeError("Can't extract the value of a Nothing.")
|
301 | }
|
302 |
|
303 | Just.prototype.get = function() {
|
304 | return this.value
|
305 | }
|
306 |
|
307 |
|
308 | /**
|
309 | * Extracts the value out of the `Maybe[α]` structure. If there is no value,
|
310 | * returns the given default.
|
311 | *
|
312 | * @method
|
313 | * @summary @Maybe[α] => α → α
|
314 | */
|
315 | Maybe.prototype.getOrElse = unimplemented
|
316 |
|
317 | Nothing.prototype.getOrElse = function(a) {
|
318 | return a
|
319 | }
|
320 |
|
321 | Just.prototype.getOrElse = function(_) {
|
322 | return this.value
|
323 | }
|
324 |
|
325 |
|
326 | /**
|
327 | * Transforms a failure into a new `Maybe[α]` structure. Does nothing if the
|
328 | * structure already contains a value.
|
329 | *
|
330 | * @method
|
331 | * @summary @Maybe[α] => (Void → Maybe[α]) → Maybe[α]
|
332 | */
|
333 | Maybe.prototype.orElse = unimplemented
|
334 |
|
335 | Nothing.prototype.orElse = function(f) {
|
336 | return f()
|
337 | }
|
338 |
|
339 | Just.prototype.orElse = function(_) {
|
340 | return this
|
341 | }
|