-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathtrait.js
More file actions
96 lines (77 loc) · 2 KB
/
trait.js
File metadata and controls
96 lines (77 loc) · 2 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
95
96
'use strict';
class Box {
#value = undefined;
constructor(value) {
this.#value = value;
}
get() {
if (this.#value !== undefined) return this.#value;
throw new Error('Moved or dropped');
}
move() {
const val = this.get();
this.#value = undefined;
return new Box(val);
}
[Symbol.dispose]() {
this.#value = undefined;
}
}
class Trait {
static #registry = new Map();
#implementations = new WeakMap();
constructor(name) {
this.name = name;
Trait.#registry.set(name, this);
}
static for(name) {
return Trait.#registry.get(name) || new Trait(name);
}
implement(target, callable) {
if (typeof target !== 'object' || target === null) {
throw new TypeError(`Target is not Object`);
}
if (typeof callable !== 'function') {
throw new TypeError(`Callable is not Function`);
}
this.#implementations.set(target, callable);
}
invoke(box, ...args) {
const target = box.get();
const callable = this.#implementations.get(target);
if (callable === undefined) {
throw new Error(`Trait not implementemented: ${this.name}`);
}
return callable(...args);
}
}
const Clonable = Trait.for('Clonable');
const Movable = Trait.for('Movable');
const Serializable = Trait.for('Serializable');
const createPoint = (x, y) => {
using point = new Box({ x, y });
const self = Object.create(null);
Clonable.implement(self, () => {
const { x, y } = point.get();
return createPoint(x, y);
});
Movable.implement(self, (d) => {
const p = point.get();
return createPoint(p.x + d.x, p.y + d.y);
});
Serializable.implement(self, () => {
const { x, y } = point.get();
return `(${x}, ${y})`;
});
return new Box(self);
};
// Usage
const main = () => {
using p1 = createPoint(10, 20);
console.log(Serializable.invoke(p1));
using c0 = Clonable.invoke(p1);
console.log(Serializable.invoke(c0));
using c1 = Movable.invoke(c0, { x: -5, y: 10 });
console.log(Serializable.invoke(c1));
};
main();