UNPKG

9.49 kBJavaScriptView Raw
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 */
25module.exports = Maybe
26
27// -- Aliases ----------------------------------------------------------
28var clone = Object.create
29var unimplemented = function(){ throw new Error('Not implemented.') }
30var 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 */
73function Maybe() {}
74
75// The case for successful values
76Just.prototype = clone(Maybe.prototype)
77function Just(a){
78 this.value = a
79}
80
81// The case for failure values
82Nothing.prototype = clone(Maybe.prototype)
83function 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 */
94Maybe.Nothing = function() {
95 return new Nothing
96}
97Maybe.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 */
108Maybe.Just = function(a) {
109 return new Just(a)
110}
111Maybe.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 */
124Maybe.fromNullable = function(a) {
125 return a != null? new Just(a)
126 : /* otherwise */ new Nothing
127}
128Maybe.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 */
138Maybe.fromEither = function(a) {
139 return a.fold(Maybe.Nothing, Maybe.Just)
140}
141Maybe.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 */
152Maybe.fromValidation = Maybe.fromEither
153Maybe.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 */
163Maybe.prototype.isNothing = false
164Nothing.prototype.isNothing = true
165
166
167/**
168 * True if the `Maybe[α]` structure contains a single value (i.e.: `Just(α)`).
169 *
170 * @summary Boolean
171 */
172Maybe.prototype.isJust = false
173Just.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 */
186Maybe.of = function(a) {
187 return Maybe.prototype.Just(a)
188}
189Maybe.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 */
202Maybe.prototype.ap = unimplemented
203
204Nothing.prototype.ap = noop
205
206Just.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 */
222Maybe.prototype.map = unimplemented
223Nothing.prototype.map = noop
224
225Just.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 */
239Maybe.prototype.chain = unimplemented
240Nothing.prototype.chain = noop
241
242Just.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 */
255Maybe.prototype.toString = unimplemented
256
257Nothing.prototype.toString = function() {
258 return 'Maybe.Nothing'
259}
260
261Just.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 */
274Maybe.prototype.isEqual = unimplemented
275
276Nothing.prototype.isEqual = function(b) {
277 return b.isNothing
278}
279
280Just.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 */
297Maybe.prototype.get = unimplemented
298
299Nothing.prototype.get = function() {
300 throw new TypeError("Can't extract the value of a Nothing.")
301}
302
303Just.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 */
315Maybe.prototype.getOrElse = unimplemented
316
317Nothing.prototype.getOrElse = function(a) {
318 return a
319}
320
321Just.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 */
333Maybe.prototype.orElse = unimplemented
334
335Nothing.prototype.orElse = function(f) {
336 return f()
337}
338
339Just.prototype.orElse = function(_) {
340 return this
341}