|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 */ |
|
5 "use strict"; |
|
6 |
|
7 module.metadata = { |
|
8 "stability": "experimental" |
|
9 }; |
|
10 |
|
11 // Disclamer: |
|
12 // In this module we'll have some common argument / variable names |
|
13 // to hint their type or behavior. |
|
14 // |
|
15 // - `f` stands for "function" that is intended to be side effect |
|
16 // free. |
|
17 // - `p` stands for "predicate" that is function which returns logical |
|
18 // true or false and is intended to be side effect free. |
|
19 // - `x` / `y` single item of the sequence. |
|
20 // - `xs` / `ys` sequence of `x` / `y` items where `x` / `y` signifies |
|
21 // type of the items in sequence, so sequence is not of the same item. |
|
22 // - `_` used for argument(s) or variable(s) who's values are ignored. |
|
23 |
|
24 const { complement, flip, identity } = require("../lang/functional"); |
|
25 const { iteratorSymbol } = require("../util/iteration"); |
|
26 const { isArray, isArguments, isMap, isSet, |
|
27 isString, isBoolean, isNumber } = require("../lang/type"); |
|
28 |
|
29 const Sequence = function Sequence(iterator) { |
|
30 if (iterator.isGenerator && iterator.isGenerator()) |
|
31 this[iteratorSymbol] = iterator; |
|
32 else |
|
33 throw TypeError("Expected generator argument"); |
|
34 }; |
|
35 exports.Sequence = Sequence; |
|
36 |
|
37 const polymorphic = dispatch => x => |
|
38 x === null ? dispatch.null(null) : |
|
39 x === void(0) ? dispatch.void(void(0)) : |
|
40 isArray(x) ? (dispatch.array || dispatch.indexed)(x) : |
|
41 isString(x) ? (dispatch.string || dispatch.indexed)(x) : |
|
42 isArguments(x) ? (dispatch.arguments || dispatch.indexed)(x) : |
|
43 isMap(x) ? dispatch.map(x) : |
|
44 isSet(x) ? dispatch.set(x) : |
|
45 isNumber(x) ? dispatch.number(x) : |
|
46 isBoolean(x) ? dispatch.boolean(x) : |
|
47 dispatch.default(x); |
|
48 |
|
49 const nogen = function*() {}; |
|
50 const empty = () => new Sequence(nogen); |
|
51 exports.empty = empty; |
|
52 |
|
53 const seq = polymorphic({ |
|
54 null: empty, |
|
55 void: empty, |
|
56 array: identity, |
|
57 string: identity, |
|
58 arguments: identity, |
|
59 map: identity, |
|
60 set: identity, |
|
61 default: x => x instanceof Sequence ? x : new Sequence(x) |
|
62 }); |
|
63 exports.seq = seq; |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 // Function to cast seq to string. |
|
69 const string = (...etc) => "".concat(...etc); |
|
70 exports.string = string; |
|
71 |
|
72 // Function for casting seq to plain object. |
|
73 const object = (...pairs) => { |
|
74 let result = {}; |
|
75 for (let [key, value] of pairs) |
|
76 result[key] = value; |
|
77 |
|
78 return result; |
|
79 }; |
|
80 exports.object = object; |
|
81 |
|
82 // Takes `getEnumerator` function that returns `nsISimpleEnumerator` |
|
83 // and creates lazy sequence of it's items. Note that function does |
|
84 // not take `nsISimpleEnumerator` itslef because that would allow |
|
85 // single iteration, which would not be consistent with rest of the |
|
86 // lazy sequences. |
|
87 const fromEnumerator = getEnumerator => seq(function* () { |
|
88 const enumerator = getEnumerator(); |
|
89 while (enumerator.hasMoreElements()) |
|
90 yield enumerator.getNext(); |
|
91 }); |
|
92 exports.fromEnumerator = fromEnumerator; |
|
93 |
|
94 // Takes `object` and returns lazy sequence of own `[key, value]` |
|
95 // pairs (does not include inherited and non enumerable keys). |
|
96 const pairs = polymorphic({ |
|
97 null: empty, |
|
98 void: empty, |
|
99 map: identity, |
|
100 indexed: indexed => seq(function* () { |
|
101 const count = indexed.length; |
|
102 let index = 0; |
|
103 while (index < count) { |
|
104 yield [index, indexed[index]]; |
|
105 index = index + 1; |
|
106 } |
|
107 }), |
|
108 default: object => seq(function* () { |
|
109 for (let key of Object.keys(object)) |
|
110 yield [key, object[key]]; |
|
111 }) |
|
112 }); |
|
113 exports.pairs = pairs; |
|
114 |
|
115 |
|
116 const keys = polymorphic({ |
|
117 null: empty, |
|
118 void: empty, |
|
119 indexed: indexed => seq(function* () { |
|
120 const count = indexed.length; |
|
121 let index = 0; |
|
122 while (index < count) { |
|
123 yield index; |
|
124 index = index + 1; |
|
125 } |
|
126 }), |
|
127 map: map => seq(function* () { |
|
128 for (let [key, _] of map) |
|
129 yield key; |
|
130 }), |
|
131 default: object => seq(function* () { |
|
132 for (let key of Object.keys(object)) |
|
133 yield key; |
|
134 }) |
|
135 }); |
|
136 exports.keys = keys; |
|
137 |
|
138 |
|
139 const values = polymorphic({ |
|
140 null: empty, |
|
141 void: empty, |
|
142 set: identity, |
|
143 indexed: indexed => seq(function* () { |
|
144 const count = indexed.length; |
|
145 let index = 0; |
|
146 while (index < count) { |
|
147 yield indexed[index]; |
|
148 index = index + 1; |
|
149 } |
|
150 }), |
|
151 map: map => seq(function* () { |
|
152 for (let [_, value] of map) yield value; |
|
153 }), |
|
154 default: object => seq(function* () { |
|
155 for (let key of Object.keys(object)) yield object[key]; |
|
156 }) |
|
157 }); |
|
158 exports.values = values; |
|
159 |
|
160 |
|
161 |
|
162 // Returns a lazy sequence of `x`, `f(x)`, `f(f(x))` etc. |
|
163 // `f` must be free of side-effects. Note that returned |
|
164 // sequence is infinite so it must be consumed partially. |
|
165 // |
|
166 // Implements clojure iterate: |
|
167 // http://clojuredocs.org/clojure_core/clojure.core/iterate |
|
168 const iterate = (f, x) => seq(function* () { |
|
169 let state = x; |
|
170 while (true) { |
|
171 yield state; |
|
172 state = f(state); |
|
173 } |
|
174 }); |
|
175 exports.iterate = iterate; |
|
176 |
|
177 // Returns a lazy sequence of the items in sequence for which `p(item)` |
|
178 // returns `true`. `p` must be free of side-effects. |
|
179 // |
|
180 // Implements clojure filter: |
|
181 // http://clojuredocs.org/clojure_core/clojure.core/filter |
|
182 const filter = (p, sequence) => seq(function* () { |
|
183 if (sequence !== null && sequence !== void(0)) { |
|
184 for (let item of sequence) { |
|
185 if (p(item)) |
|
186 yield item; |
|
187 } |
|
188 } |
|
189 }); |
|
190 exports.filter = filter; |
|
191 |
|
192 // Returns a lazy sequence consisting of the result of applying `f` to the |
|
193 // set of first items of each sequence, followed by applying f to the set |
|
194 // of second items in each sequence, until any one of the sequences is |
|
195 // exhausted. Any remaining items in other sequences are ignored. Function |
|
196 // `f` should accept number-of-sequences arguments. |
|
197 // |
|
198 // Implements clojure map: |
|
199 // http://clojuredocs.org/clojure_core/clojure.core/map |
|
200 const map = (f, ...sequences) => seq(function* () { |
|
201 const count = sequences.length; |
|
202 // Optimize a single sequence case |
|
203 if (count === 1) { |
|
204 let [sequence] = sequences; |
|
205 if (sequence !== null && sequence !== void(0)) { |
|
206 for (let item of sequence) |
|
207 yield f(item); |
|
208 } |
|
209 } |
|
210 else { |
|
211 // define args array that will be recycled on each |
|
212 // step to aggregate arguments to be passed to `f`. |
|
213 let args = []; |
|
214 // define inputs to contain started generators. |
|
215 let inputs = []; |
|
216 |
|
217 let index = 0; |
|
218 while (index < count) { |
|
219 inputs[index] = sequences[index][iteratorSymbol](); |
|
220 index = index + 1; |
|
221 } |
|
222 |
|
223 // Run loop yielding of applying `f` to the set of |
|
224 // items at each step until one of the `inputs` is |
|
225 // exhausted. |
|
226 let done = false; |
|
227 while (!done) { |
|
228 let index = 0; |
|
229 let value = void(0); |
|
230 while (index < count && !done) { |
|
231 ({ done, value }) = inputs[index].next(); |
|
232 |
|
233 // If input is not exhausted yet store value in args. |
|
234 if (!done) { |
|
235 args[index] = value; |
|
236 index = index + 1; |
|
237 } |
|
238 } |
|
239 |
|
240 // If none of the inputs is exhasted yet, `args` contain items |
|
241 // from each input so we yield application of `f` over them. |
|
242 if (!done) |
|
243 yield f(...args); |
|
244 } |
|
245 } |
|
246 }); |
|
247 exports.map = map; |
|
248 |
|
249 // Returns a lazy sequence of the intermediate values of the reduction (as |
|
250 // per reduce) of sequence by `f`, starting with `initial` value if provided. |
|
251 // |
|
252 // Implements clojure reductions: |
|
253 // http://clojuredocs.org/clojure_core/clojure.core/reductions |
|
254 const reductions = (...params) => { |
|
255 const count = params.length; |
|
256 let hasInitial = false; |
|
257 let f, initial, source; |
|
258 if (count === 2) { |
|
259 ([f, source]) = params; |
|
260 } |
|
261 else if (count === 3) { |
|
262 ([f, initial, source]) = params; |
|
263 hasInitial = true; |
|
264 } |
|
265 else { |
|
266 throw Error("Invoked with wrong number of arguments: " + count); |
|
267 } |
|
268 |
|
269 const sequence = seq(source); |
|
270 |
|
271 return seq(function* () { |
|
272 let started = hasInitial; |
|
273 let result = void(0); |
|
274 |
|
275 // If initial is present yield it. |
|
276 if (hasInitial) |
|
277 yield (result = initial); |
|
278 |
|
279 // For each item of the sequence accumulate new result. |
|
280 for (let item of sequence) { |
|
281 // If nothing has being yield yet set result to first |
|
282 // item and yield it. |
|
283 if (!started) { |
|
284 started = true; |
|
285 yield (result = item); |
|
286 } |
|
287 // Otherwise accumulate new result and yield it. |
|
288 else { |
|
289 yield (result = f(result, item)); |
|
290 } |
|
291 } |
|
292 |
|
293 // If nothing has being yield yet it's empty sequence and no |
|
294 // `initial` was provided in which case we need to yield `f()`. |
|
295 if (!started) |
|
296 yield f(); |
|
297 }); |
|
298 }; |
|
299 exports.reductions = reductions; |
|
300 |
|
301 // `f` should be a function of 2 arguments. If `initial` is not supplied, |
|
302 // returns the result of applying `f` to the first 2 items in sequence, then |
|
303 // applying `f` to that result and the 3rd item, etc. If sequence contains no |
|
304 // items, `f` must accept no arguments as well, and reduce returns the |
|
305 // result of calling f with no arguments. If sequence has only 1 item, it |
|
306 // is returned and `f` is not called. If `initial` is supplied, returns the |
|
307 // result of applying `f` to `initial` and the first item in sequence, then |
|
308 // applying `f` to that result and the 2nd item, etc. If sequence contains no |
|
309 // items, returns `initial` and `f` is not called. |
|
310 // |
|
311 // Implements clojure reduce: |
|
312 // http://clojuredocs.org/clojure_core/clojure.core/reduce |
|
313 const reduce = (...args) => { |
|
314 const xs = reductions(...args); |
|
315 let x; |
|
316 for (x of xs) void(0); |
|
317 return x; |
|
318 }; |
|
319 exports.reduce = reduce; |
|
320 |
|
321 const each = (f, sequence) => { |
|
322 for (let x of seq(sequence)) void(f(x)); |
|
323 }; |
|
324 exports.each = each; |
|
325 |
|
326 |
|
327 const inc = x => x + 1; |
|
328 // Returns the number of items in the sequence. `count(null)` && `count()` |
|
329 // returns `0`. Also works on strings, arrays, Maps & Sets. |
|
330 |
|
331 // Implements clojure count: |
|
332 // http://clojuredocs.org/clojure_core/clojure.core/count |
|
333 const count = polymorphic({ |
|
334 null: _ => 0, |
|
335 void: _ => 0, |
|
336 indexed: indexed => indexed.length, |
|
337 map: map => map.size, |
|
338 set: set => set.size, |
|
339 default: xs => reduce(inc, 0, xs) |
|
340 }); |
|
341 exports.count = count; |
|
342 |
|
343 // Returns `true` if sequence has no items. |
|
344 |
|
345 // Implements clojure empty?: |
|
346 // http://clojuredocs.org/clojure_core/clojure.core/empty_q |
|
347 const isEmpty = sequence => { |
|
348 // Treat `null` and `undefined` as empty sequences. |
|
349 if (sequence === null || sequence === void(0)) |
|
350 return true; |
|
351 |
|
352 // If contains any item non empty so return `false`. |
|
353 for (let _ of sequence) |
|
354 return false; |
|
355 |
|
356 // If has not returned yet, there was nothing to iterate |
|
357 // so it's empty. |
|
358 return true; |
|
359 }; |
|
360 exports.isEmpty = isEmpty; |
|
361 |
|
362 const and = (a, b) => a && b; |
|
363 |
|
364 // Returns true if `p(x)` is logical `true` for every `x` in sequence, else |
|
365 // `false`. |
|
366 // |
|
367 // Implements clojure every?: |
|
368 // http://clojuredocs.org/clojure_core/clojure.core/every_q |
|
369 const isEvery = (p, sequence) => { |
|
370 if (sequence !== null && sequence !== void(0)) { |
|
371 for (let item of sequence) { |
|
372 if (!p(item)) |
|
373 return false; |
|
374 } |
|
375 } |
|
376 return true; |
|
377 }; |
|
378 exports.isEvery = isEvery; |
|
379 |
|
380 // Returns the first logical true value of (p x) for any x in sequence, |
|
381 // else `null`. |
|
382 // |
|
383 // Implements clojure some: |
|
384 // http://clojuredocs.org/clojure_core/clojure.core/some |
|
385 const some = (p, sequence) => { |
|
386 if (sequence !== null && sequence !== void(0)) { |
|
387 for (let item of sequence) { |
|
388 if (p(item)) |
|
389 return true; |
|
390 } |
|
391 } |
|
392 return null; |
|
393 }; |
|
394 exports.some = some; |
|
395 |
|
396 // Returns a lazy sequence of the first `n` items in sequence, or all items if |
|
397 // there are fewer than `n`. |
|
398 // |
|
399 // Implements clojure take: |
|
400 // http://clojuredocs.org/clojure_core/clojure.core/take |
|
401 const take = (n, sequence) => n <= 0 ? empty() : seq(function* () { |
|
402 let count = n; |
|
403 for (let item of sequence) { |
|
404 yield item; |
|
405 count = count - 1; |
|
406 if (count === 0) break; |
|
407 } |
|
408 }); |
|
409 exports.take = take; |
|
410 |
|
411 // Returns a lazy sequence of successive items from sequence while |
|
412 // `p(item)` returns `true`. `p` must be free of side-effects. |
|
413 // |
|
414 // Implements clojure take-while: |
|
415 // http://clojuredocs.org/clojure_core/clojure.core/take-while |
|
416 const takeWhile = (p, sequence) => seq(function* () { |
|
417 for (let item of sequence) { |
|
418 if (!p(item)) |
|
419 break; |
|
420 |
|
421 yield item; |
|
422 } |
|
423 }); |
|
424 exports.takeWhile = takeWhile; |
|
425 |
|
426 // Returns a lazy sequence of all but the first `n` items in |
|
427 // sequence. |
|
428 // |
|
429 // Implements clojure drop: |
|
430 // http://clojuredocs.org/clojure_core/clojure.core/drop |
|
431 const drop = (n, sequence) => seq(function* () { |
|
432 if (sequence !== null && sequence !== void(0)) { |
|
433 let count = n; |
|
434 for (let item of sequence) { |
|
435 if (count > 0) |
|
436 count = count - 1; |
|
437 else |
|
438 yield item; |
|
439 } |
|
440 } |
|
441 }); |
|
442 exports.drop = drop; |
|
443 |
|
444 // Returns a lazy sequence of the items in sequence starting from the |
|
445 // first item for which `p(item)` returns falsy value. |
|
446 // |
|
447 // Implements clojure drop-while: |
|
448 // http://clojuredocs.org/clojure_core/clojure.core/drop-while |
|
449 const dropWhile = (p, sequence) => seq(function* () { |
|
450 let keep = false; |
|
451 for (let item of sequence) { |
|
452 keep = keep || !p(item); |
|
453 if (keep) yield item; |
|
454 } |
|
455 }); |
|
456 exports.dropWhile = dropWhile; |
|
457 |
|
458 // Returns a lazy sequence representing the concatenation of the |
|
459 // suplied sequences. |
|
460 // |
|
461 // Implements clojure conact: |
|
462 // http://clojuredocs.org/clojure_core/clojure.core/concat |
|
463 const concat = (...sequences) => seq(function* () { |
|
464 for (let sequence of sequences) |
|
465 for (let item of sequence) |
|
466 yield item; |
|
467 }); |
|
468 exports.concat = concat; |
|
469 |
|
470 // Returns the first item in the sequence. |
|
471 // |
|
472 // Implements clojure first: |
|
473 // http://clojuredocs.org/clojure_core/clojure.core/first |
|
474 const first = sequence => { |
|
475 if (sequence !== null && sequence !== void(0)) { |
|
476 for (let item of sequence) |
|
477 return item; |
|
478 } |
|
479 return null; |
|
480 }; |
|
481 exports.first = first; |
|
482 |
|
483 // Returns a possibly empty sequence of the items after the first. |
|
484 // |
|
485 // Implements clojure rest: |
|
486 // http://clojuredocs.org/clojure_core/clojure.core/rest |
|
487 const rest = sequence => drop(1, sequence); |
|
488 exports.rest = rest; |
|
489 |
|
490 // Returns the value at the index. Returns `notFound` or `undefined` |
|
491 // if index is out of bounds. |
|
492 const nth = (xs, n, notFound) => { |
|
493 if (n >= 0) { |
|
494 if (isArray(xs) || isArguments(xs) || isString(xs)) { |
|
495 return n < xs.length ? xs[n] : notFound; |
|
496 } |
|
497 else if (xs !== null && xs !== void(0)) { |
|
498 let count = n; |
|
499 for (let x of xs) { |
|
500 if (count <= 0) |
|
501 return x; |
|
502 |
|
503 count = count - 1; |
|
504 } |
|
505 } |
|
506 } |
|
507 return notFound; |
|
508 }; |
|
509 exports.nth = nth; |
|
510 |
|
511 // Return the last item in sequence, in linear time. |
|
512 // If `sequence` is an array or string or arguments |
|
513 // returns in constant time. |
|
514 // Implements clojure last: |
|
515 // http://clojuredocs.org/clojure_core/clojure.core/last |
|
516 const last = polymorphic({ |
|
517 null: _ => null, |
|
518 void: _ => null, |
|
519 indexed: indexed => indexed[indexed.length - 1], |
|
520 map: xs => reduce((_, x) => x, xs), |
|
521 set: xs => reduce((_, x) => x, xs), |
|
522 default: xs => reduce((_, x) => x, xs) |
|
523 }); |
|
524 exports.last = last; |
|
525 |
|
526 // Return a lazy sequence of all but the last `n` (default 1) items |
|
527 // from the give `xs`. |
|
528 // |
|
529 // Implements clojure drop-last: |
|
530 // http://clojuredocs.org/clojure_core/clojure.core/drop-last |
|
531 const dropLast = flip((xs, n=1) => seq(function* () { |
|
532 let ys = []; |
|
533 for (let x of xs) { |
|
534 ys.push(x); |
|
535 if (ys.length > n) |
|
536 yield ys.shift(); |
|
537 } |
|
538 })); |
|
539 exports.dropLast = dropLast; |
|
540 |
|
541 // Returns a lazy sequence of the elements of `xs` with duplicates |
|
542 // removed |
|
543 // |
|
544 // Implements clojure distinct |
|
545 // http://clojuredocs.org/clojure_core/clojure.core/distinct |
|
546 const distinct = sequence => seq(function* () { |
|
547 let items = new Set(); |
|
548 for (let item of sequence) { |
|
549 if (!items.has(item)) { |
|
550 items.add(item); |
|
551 yield item; |
|
552 } |
|
553 } |
|
554 }); |
|
555 exports.distinct = distinct; |
|
556 |
|
557 // Returns a lazy sequence of the items in `xs` for which |
|
558 // `p(x)` returns false. `p` must be free of side-effects. |
|
559 // |
|
560 // Implements clojure remove |
|
561 // http://clojuredocs.org/clojure_core/clojure.core/remove |
|
562 const remove = (p, xs) => filter(complement(p), xs); |
|
563 exports.remove = remove; |
|
564 |
|
565 // Returns the result of applying concat to the result of |
|
566 // `map(f, xs)`. Thus function `f` should return a sequence. |
|
567 // |
|
568 // Implements clojure mapcat |
|
569 // http://clojuredocs.org/clojure_core/clojure.core/mapcat |
|
570 const mapcat = (f, sequence) => seq(function* () { |
|
571 const sequences = map(f, sequence); |
|
572 for (let sequence of sequences) |
|
573 for (let item of sequence) |
|
574 yield item; |
|
575 }); |
|
576 exports.mapcat = mapcat; |