Skip to content

Commit 0a4429d

Browse files
committed
Add multimethods as opt-in runtime
defmulti, defmethod, hierarchy ops (isa?, derive, underive, parents, ancestors, descendants, make-hierarchy), and method-table ops (get-method, methods, remove-method, prefer-method, prefers) live in a new src/squint/multi.js module, imported only when one of those forms appears in user code — programs that don't use multimethods pay zero bundle cost.
1 parent b88df15 commit 0a4429d

6 files changed

Lines changed: 478 additions & 3 deletions

File tree

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"src/squint/set.js",
1212
"src/squint/html.js",
1313
"src/squint/math.js",
14+
"src/squint/multi.js",
1415
"src/squint/test.js",
1516
"lib",
1617
"node_cli.js",
@@ -61,6 +62,7 @@
6162
"./src/squint/string.js": "./src/squint/string.js",
6263
"./node-api.js": "./node-api.js",
6364
"./src/squint/html.js": "./src/squint/html.js",
65+
"./src/squint/multi.js": "./src/squint/multi.js",
6466
"./src/squint/test.js": "./src/squint/test.js"
6567
}
6668
}

src/squint/compiler.cljc

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
[squint.internal.fn :refer [core-defmacro core-defn core-defn- core-fn]]
2525
[squint.internal.loop :as loop]
2626
[squint.internal.macros :as macros]
27+
[squint.internal.multi :as multi]
2728
[squint.internal.protocols :as protocols]
2829
[squint.internal.test :as test])
2930
#?(:cljs (:require-macros [squint.resource :refer [edn-resource]])))
@@ -103,7 +104,22 @@
103104
'assoc macros/assoc-inline
104105
'assoc! macros/assoc!-inline
105106
'get macros/get-inline
106-
'vswap! macros/vswap!}
107+
'vswap! macros/vswap!
108+
'defmulti multi/core-defmulti
109+
'defmethod multi/core-defmethod
110+
'get-method multi/core-get-method
111+
'methods multi/core-methods
112+
'remove-method multi/core-remove-method
113+
'remove-all-methods multi/core-remove-all-methods
114+
'prefer-method multi/core-prefer-method
115+
'prefers multi/core-prefers
116+
'isa? multi/core-isa?
117+
'derive multi/core-derive
118+
'underive multi/core-underive
119+
'make-hierarchy multi/core-make-hierarchy
120+
'parents multi/core-parents
121+
'ancestors multi/core-ancestors
122+
'descendants multi/core-descendants}
107123
cc/common-macros))
108124

109125
(def built-in-macro-nss
@@ -452,6 +468,7 @@
452468
cc/*repl* (:repl opts cc/*repl*)]
453469
(let [core-package (get import-maps cc/*core-package* cc/*core-package*)
454470
need-html-import (atom false)
471+
need-multi-import (atom false)
455472
opts (merge {:ns-state (atom {})
456473
:top-level true} opts)
457474
imported-vars (atom {})
@@ -478,7 +495,8 @@
478495
:imports imports
479496
:jsx false
480497
:pragmas pragmas
481-
:need-html-import need-html-import))
498+
:need-html-import need-html-import
499+
:need-multi-import need-multi-import))
482500
jsx *jsx*
483501
_ (when (and jsx jsx-runtime)
484502
(swap! imports str
@@ -500,6 +518,13 @@
500518
(if cc/*repl*
501519
(format "var squint_html = await import('%s');\n" html-pkg)
502520
(format "import * as squint_html from '%s';\n" html-pkg)))))
521+
_ (when @need-multi-import
522+
(swap! imports str
523+
(let [multi-pkg "squint-cljs/src/squint/multi.js"
524+
multi-pkg (get import-maps multi-pkg multi-pkg)]
525+
(if cc/*repl*
526+
(format "var squint_multi = await import('%s');\n" multi-pkg)
527+
(format "import * as squint_multi from '%s';\n" multi-pkg)))))
503528
pragmas (:js @pragmas)
504529
imports (when-not elide-imports @imports)
505530
exports (when-not elide-exports

src/squint/internal/multi.cljc

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
(ns squint.internal.multi
2+
(:refer-clojure :exclude [defmulti defmethod remove-method
3+
prefer-method prefers methods get-method
4+
remove-all-methods
5+
isa? derive underive make-hierarchy
6+
parents ancestors descendants])
7+
(:require [clojure.core :as core]
8+
[clojure.string :as str]))
9+
10+
(core/defn- flag! [env]
11+
(when-let [atm (:need-multi-import env)]
12+
(reset! atm true)))
13+
14+
(core/defn- multi-call [js-name args]
15+
(let [tmpl (str "squint_multi." js-name "("
16+
(str/join ", " (repeat (count args) "~{}"))
17+
")")]
18+
`(~'js* ~tmpl ~@args)))
19+
20+
(core/defn core-defmulti
21+
"(defmulti name dispatch-fn) or
22+
(defmulti name docstring? attr-map? dispatch-fn & options)"
23+
[_&form env mm-name & args]
24+
(flag! env)
25+
(let [[args docstring] (if (string? (first args))
26+
[(next args) (first args)]
27+
[args nil])
28+
[args attr-map] (if (map? (first args))
29+
[(next args) (first args)]
30+
[args nil])
31+
dispatch-fn (first args)
32+
opts (apply hash-map (next args))
33+
opts-js (cond-> {}
34+
(contains? opts :default) (assoc "default" (:default opts))
35+
(contains? opts :hierarchy) (assoc "hierarchy" (:hierarchy opts)))
36+
m (merge (if docstring {:doc docstring} {}) attr-map)
37+
mm-name* (if (seq m) (with-meta mm-name m) mm-name)]
38+
`(def ~mm-name* ~(multi-call "defmulti" [(str mm-name) dispatch-fn opts-js]))))
39+
40+
(core/defn core-defmethod
41+
"(defmethod multifn dispatch-val [args*] body)"
42+
[_&form env multifn dispatch-val & fn-tail]
43+
(flag! env)
44+
(multi-call "defmethod" [multifn dispatch-val `(fn ~@fn-tail)]))
45+
46+
(core/defn core-get-method [_&form env mf dv]
47+
(flag! env)
48+
(multi-call "get_method" [mf dv]))
49+
50+
(core/defn core-methods [_&form env mf]
51+
(flag! env)
52+
(multi-call "methods" [mf]))
53+
54+
(core/defn core-remove-method [_&form env mf dv]
55+
(flag! env)
56+
(multi-call "remove_method" [mf dv]))
57+
58+
(core/defn core-remove-all-methods [_&form env mf]
59+
(flag! env)
60+
(multi-call "remove_all_methods" [mf]))
61+
62+
(core/defn core-prefer-method [_&form env mf a b]
63+
(flag! env)
64+
(multi-call "prefer_method" [mf a b]))
65+
66+
(core/defn core-prefers [_&form env mf]
67+
(flag! env)
68+
(multi-call "prefers" [mf]))
69+
70+
(core/defn core-isa? [_&form env & args]
71+
(flag! env)
72+
(multi-call "isa_QMARK_" args))
73+
74+
(core/defn core-derive [_&form env & args]
75+
(flag! env)
76+
(multi-call "derive" args))
77+
78+
(core/defn core-underive [_&form env & args]
79+
(flag! env)
80+
(multi-call "underive" args))
81+
82+
(core/defn core-make-hierarchy [_&form env]
83+
(flag! env)
84+
(multi-call "make_hierarchy" []))
85+
86+
(core/defn core-parents [_&form env & args]
87+
(flag! env)
88+
(multi-call "parents" args))
89+
90+
(core/defn core-ancestors [_&form env & args]
91+
(flag! env)
92+
(multi-call "ancestors" args))
93+
94+
(core/defn core-descendants [_&form env & args]
95+
(flag! env)
96+
(multi-call "descendants" args))

0 commit comments

Comments
 (0)