git.fiddlerwoaroof.com
Browse code

Prototype implementation based on AMOP

Ed Langley authored on 05/09/2018 04:43:15
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,10 @@
1
+<!doctype html>
2
+<html lang="en">
3
+    <head>
4
+        <meta charset="UTF-8"/>
5
+        <title>Document</title>
6
+    </head>
7
+    <body>
8
+        <script src="./genfuns.js"></script>
9
+    </body>
10
+</html>
0 11
new file mode 100644
... ...
@@ -0,0 +1,183 @@
1
+let genfun_prototype = {
2
+    name: "(placeholder)",
3
+    lambda_list: [],
4
+    methods: [],
5
+};
6
+
7
+function GenericFunction(name, lambda_list, methods) {
8
+    if (! (this instanceof GenericFunction) ) {
9
+        return new GenericFunction(name, lambda_list, methods);
10
+    }
11
+
12
+    this.name = name;
13
+    this.lambda_list = lambda_list;
14
+    this.methods = methods;
15
+}
16
+
17
+GenericFunction.prototype = Object.assign(
18
+    Object.create(genfun_prototype), {
19
+    }
20
+);
21
+
22
+let method_prototype = {
23
+    lambda_list: [],
24
+    qualifiers: [],
25
+    specializrs: [],
26
+    body() { throw new Error('Unimplemented'); },
27
+    environment: {},
28
+    generic_function: {},
29
+};
30
+
31
+function StandardMethod(
32
+    lambda_list, qualifiers, specializers, body, environment, generic_function
33
+) {
34
+    if (! (this instanceof StandardMethod) ) {
35
+        return new StandardMethod(...arguments);
36
+    }
37
+
38
+    this.lambda_list = lambda_list;
39
+    this.qualifiers = qualifiers;
40
+    this.specializers = specializers;
41
+    this.body = body;
42
+    this.environment = environment;
43
+    this.generic_function = generic_function;
44
+}
45
+
46
+function ensure_method(gf, ...rest) {
47
+    let new_method = StandardMethod(...rest);
48
+    add_method(gf, new_method);
49
+    return new_method;
50
+}
51
+
52
+function add_method(gf, method) {
53
+    method.generic_function = gf;
54
+    gf.methods.push(method);
55
+    return method;
56
+}
57
+
58
+function classes_of(args) {
59
+    return args.map(Object.getPrototypeOf);
60
+}
61
+
62
+const required_portion = x => x;
63
+
64
+function apply_generic_function(gf, args) {
65
+    let applicable_methods =
66
+        compute_applicable_methods_using_classes(gf, required_portion(args));
67
+    if (applicable_methods.length === 0) {
68
+        throw new Error(`no applicable methods for gf ${gf.name} with args ${args}`);
69
+    } else {
70
+        return apply_methods(gf, args, applicable_methods);
71
+    }
72
+}
73
+
74
+function method_more_specific_p(m1, m2, required_classes) {
75
+    const m1specializers = m1.specializers;
76
+    const m2specializers = m2.specializers;
77
+
78
+    for ([spec1, spec2] of m1specializers.map((el, idx) => [el, m2specializers[idx]])) {
79
+        if (spec1 !== spec2) {
80
+            return sub_specializer_p(spec1, spec2);
81
+        }
82
+    }
83
+}
84
+
85
+function sub_specializer_p(c1, c2) {
86
+    return c1.isPrototypeOf(c2);
87
+}
88
+
89
+const idS = Symbol.for('id');
90
+Object.prototype[idS] = function () { return this };
91
+
92
+function compute_applicable_methods_using_classes(gf, required_classes) {
93
+    const applicable_methods = gf.methods.filter(
94
+        method =>
95
+            method.specializers.every((specializer,idx) => (console.info(specializer),
96
+                                                            specializer.prototype.isPrototypeOf(required_classes[idx])
97
+                                                            || specializer.prototype.isPrototypeOf(required_classes[idx][idS]())))
98
+    );
99
+
100
+    applicable_methods.sort((a,b) => {
101
+        if (method_more_specific_p(a,b)) {
102
+            return -1;
103
+        }
104
+        if (method_more_specific_p(b,a)) {
105
+            return 1;
106
+        }
107
+        return 0;
108
+    })
109
+
110
+    return applicable_methods;
111
+}
112
+
113
+const before_qualifier = Symbol.for('before');
114
+const after_qualifier = Symbol.for('after');
115
+
116
+function arr_eq(a1, a2) {
117
+    if (a1.length !== a2.length) {
118
+        return false;
119
+    } else {
120
+        for (let x = 0; x < a1.length; x++) {
121
+            if (a1[x] instanceof Array && a2[x] instanceof Array) {
122
+                if (!arr_eq(a1[x], a2[x])) {
123
+                    return false;
124
+                }
125
+            } else if (a1[x] !== a2[x]) {
126
+                return false;
127
+            }
128
+        }
129
+        return true;
130
+    }
131
+}
132
+
133
+const primary_method_p = method => method.qualifiers.length === 0;
134
+const before_method_p = method => arr_eq(method.qualifiers, [before_qualifier]);
135
+const after_method_p = method => arr_eq(method.qualifiers, [after_qualifier]);
136
+
137
+function apply_methods(gf, args, applicable_methods) {
138
+    const primaries = applicable_methods.filter(primary_method_p);
139
+    const befores = applicable_methods.filter(before_method_p);
140
+    const afters = applicable_methods.filter(after_method_p);
141
+    afters.reverse();
142
+
143
+    if (primaries.length === 0) {
144
+        throw new Error(`No primary method for ${gf.name}`);
145
+    }
146
+
147
+    for (let before of befores) {
148
+        apply_method(before, args, []);
149
+    }
150
+
151
+    const result = apply_method(primaries[0], args, primaries.slice(1));
152
+
153
+    for (let after of afters) {
154
+        apply_method(after, args, []);
155
+    }
156
+
157
+    return result;
158
+}
159
+
160
+function apply_method(method, args, next_methods) {
161
+    return Function('call_next_method', 'next_method_p', `return ${method.body.toString()}`)(
162
+        (...cnm_args) => {
163
+            if (next_methods.length === 0) {
164
+                throw new Error(`no next method for genfun ${method.generic_function.name}`);
165
+            }
166
+
167
+            return apply_methods(method.generic_function, cnm_args.length > 0 ? cnm_args : args, next_methods);
168
+        },
169
+        () => next_methods.length === 0
170
+    )(...args);
171
+}
172
+
173
+const gf = GenericFunction("foobar", ["a", "b"], []);
174
+ensure_method(
175
+    gf,
176
+    [], [], [Object, Array], (thing, arr) => [thing, ...arr], null
177
+);
178
+ensure_method(
179
+    gf,
180
+    [], [], [Object, Object], (thing, single) => [thing, single], null
181
+);
182
+
183
+console.info(apply_generic_function(gf, [["asdf"],2]));