-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathtuple.js
More file actions
94 lines (81 loc) · 2.95 KB
/
tuple.js
File metadata and controls
94 lines (81 loc) · 2.95 KB
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
// See [`lookup.js`](lookup.html).
import { lookup, lookupArray } from "./lookup.js";
// See [`util.js`](util.html).
import {
brand,
def,
freeze,
forEachArrayMethod,
} from "./util.js";
// When called with any number of arguments, this function returns an
// object that inherits from `tuple.prototype` and is guaranteed to be
// `===` any other `tuple` object that has exactly the same items. In
// computer science jargon, `tuple` instances are "internalized" or just
// "interned," which allows for constant-time equality checking, and makes
// it possible for tuple objects to be used as `Map` or `WeakMap` keys, or
// stored in a `Set`.
export default function tuple() {
const node = lookup.apply(null, arguments);
if (node.tuple) {
return node.tuple;
}
const t = Object.create(tuple.prototype);
// Define immutable items with numeric indexes, and permanently fix the
// `.length` property.
const argc = arguments.length;
for (let i = 0; i < argc; ++i) {
t[i] = arguments[i];
}
def(t, "length", argc, false);
// Remember this new `tuple` object so that we can return the same object
// earlier next time.
return freeze(node.tuple = t);
}
// Named imports work as well as `default` imports.
export { tuple, lookup, lookupArray };
export { UniversalWeakMap } from "./universal-weak-map.js";
// Since the `immutable-tuple` package could be installed multiple times
// in an application, there is no guarantee that the `tuple` constructor
// or `tuple.prototype` will be unique, so `value instanceof tuple` is
// unreliable. Instead, to test if a value is a tuple, you should use
// `tuple.isTuple(value)`.
def(tuple.prototype, brand, true, false);
function isTuple(that) {
return !! (that && that[brand] === true);
}
tuple.isTuple = isTuple;
function toArray(tuple) {
const array = [];
let i = tuple.length;
while (i--) array[i] = tuple[i];
return array;
}
// Copy all generic non-destructive Array methods to `tuple.prototype`.
// This works because (for example) `Array.prototype.slice` can be invoked
// against any `Array`-like object.
forEachArrayMethod((name, desc, mustConvertThisToArray) => {
const method = desc && desc.value;
if (typeof method === "function") {
desc.value = function (...args) {
const result = method.apply(
mustConvertThisToArray ? toArray(this) : this,
args
);
// Of course, `tuple.prototype.slice` should return a `tuple` object,
// not a new `Array`.
return Array.isArray(result) ? tuple(...result) : result;
};
Object.defineProperty(tuple.prototype, name, desc);
}
});
// Like `Array.prototype.concat`, except for the extra effort required to
// convert any tuple arguments to arrays, so that
// ```
// tuple(1).concat(tuple(2), 3) === tuple(1, 2, 3)
// ```
const { concat } = Array.prototype;
tuple.prototype.concat = function (...args) {
return tuple(...concat.apply(toArray(this), args.map(
item => isTuple(item) ? toArray(item) : item
)));
};