sci 2025-08-13

Has anyone written sci cljs bindings for core.match? I’m looking to add them to a forked nbb. I tried emulating the https://github.com/babashka/babashka/blob/master/feature-core-match/babashka/impl/match.clj with https://github.com/logseq/nbb-logseq/blob/add-core-match-failing/features/core.match/src/nbb/impl/core_match.cljs. However this failed with Unable to resolve var match/match. I thought I’d read somewhere that copy-var works for macros. In the meantime, I have a hacky approach I’ll expand on in a thread

Gotcha. The https://github.com/clojure/core.match/blob/753e8a30de9c487ceba8ff7d39435bf87edff505/src/main/clojure/cljs/core/match.clj#L19-L32 macro is dependent on a clojure var, https://github.com/clojure/core.match/blob/753e8a30de9c487ceba8ff7d39435bf87edff505/src/main/clojure/clojure/core/match.clj#L2083-L2096 ,which is at the bottom of a 2k line jvm ns. The fork is basically doing the work of converting that jvm ns to be cljs compatible. I’ll give the fork a try

I ran tests on viebel’s fork. Good news is that most match tests. Bad news is that ones involving a map fail hard because cljs.core/ILookup doesn’t exist. If you’re curious, you can checkout https://github.com/logseq/nbb-logseq/tree/add-core-match and just run bb test as the nbb compiled assets are included. I looked at modifying the ILookup calls in the fork but there are about https://github.com/viebel/abbinare/blob/master/src/main/clojure/cljs/core/match.cljc . Going down this path seems brittle and error prone. Unless I’m missing something, I’ll look at ripping out core.match for the code I’m making nbb compatible. I can add this use case to https://github.com/babashka/sci/issues/639 if it seems helpful

this is only applicable if you are trying to run core.match from source? your original post was about SCI CLJS bindings (meaning configuration) which is a different use case?

Not sure I follow. I’m building nbb-logseq with https://github.com/logseq/nbb-logseq/blob/add-core-match/features/core.match/src/nbb/impl/core_match.cljs . When I run https://github.com/logseq/nbb-logseq/blob/add-core-match/test/core_match_match_test.cljs with nbb-logseq (via bb test), nbb fails hard with Could not resolve symbol: cljs.core/ILookup

is this because a macro expands into that symbol?

Possibly. The full failure looks like:

Running core.match tests...
----- Error --------------------------------------
Message:  Could not resolve symbol: cljs.core/ILookup
Location: /Users/me/code/work/nbb-logseq/test/core_match_match_test.cljs:110:11
Phase:    analysis

----- Context ------------------------------------
106: (deftest test-map-patterns
107:   (testing "Map patterns"
108:     (is
109:      (= (let [x {:a 1 :b 1}]
110:           (match [x]
               ^--- Could not resolve symbol: cljs.core/ILookup
111:             [{:a _ :b 2}] :a0
112:             [{:a 1 :b 1}] :a1
113:             [{:c 3 :d _ :e 4}] :a2
114:             :else nil))
115:         :a1))
The sci bindings compile https://github.com/viebel/abbinare/blob/master/src/main/clojure/cljs/core/match.cljc which has about a dozen references to cljs.core/ILookup

you can at least add ILookup to nbb.core's core config to get past this

probably still won't work but let's see what the next step is

Added ILookup to nbb.core and pushed to https://github.com/logseq/nbb-logseq/tree/add-core-match. Tests can now run with bb test but understandably 20 of 91 assertions now fail

failing how? more info would be helpful

Sure. It seems tests are failing since maps aren’t matching like they should. Here’s the full test output:

$ bb test
Running cljs-time tests...

Testing cljs-time-format-test

Ran 1 tests containing 19 assertions.
0 failures, 0 errors.
Running core.match tests...
benchmarking
basic Maranget example hand written
2866665
"Elapsed time: 518.914458 msecs"
basic Maranget example
"Elapsed time: 2768.185958 msecs"

Testing core-match-match-test

FAIL in (test-map-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:109)
Map patterns
expected: (= (let [x {:a 1, :b 1}] (match [x] [{:a _, :b 2}] :a0 [{:a 1, :b 1}] :a1 [{:c 3, :d _, :e 4}] :a2 :else nil)) :a1)
  actual: (not (= nil :a1))

FAIL in (test-map-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:117)
Map patterns
expected: (= (let [x {:a 1, :b 2}] (match [x] [{:a _, :b 2}] :a0 [{:a 1, :b 1}] :a1 [{:c 3, :d _, :e 4}] :a2 :else nil)) :a0)
  actual: (not (= nil :a0))

FAIL in (test-map-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:125)
Map patterns
expected: (= (let [x {:c 3, :d 9, :e 4}] (match [x] [{:a _, :b 2}] :a0 [{:a 1, :b 1}] :a1 [{:c 3, :d _, :e 4}] :a2 :else nil)) :a2)
  actual: (not (= nil :a2))

FAIL in (test-map-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:141)
Map patterns
expected: (= (let [x {:a 1, :b 1}] (match [x] [{:a _, :b 1}] :a0 [{:a 1, :b _}] :a1 [{:c 3, :d _, :e 4}] :a2 :else [])) :a0)
  actual: (not (= [] :a0))

FAIL in (test-map-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:149)
Map patterns
expected: (= (let [x {:a 1, :b 1, :c 1}] (match [x] [{:a _, :b 2}] :a0 [{:a 1, :b _}] :a1 [{:c 3, :d _, :e 4}] :a2 :else [])) :a1)
  actual: (not (= [] :a1))

FAIL in (test-map-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:157)
Map patterns
expected: (= (let [x {:a 1, :b 1}] (match [x] [{:a _, :b 2}] :a0 [{:a _, :b _}] :a1 [{:c 3, :d _, :e 4}] :a2 :else [])) :a1)
  actual: (not (= [] :a1))

FAIL in (test-map-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:173)
Map patterns
expected: (= (let [x {:a 1, :b 1}] (match [x] [{:b 1}] :a0 [{:a _, :b _}] :a1 [{:a _, :b _}] :a2 :else [])) :a0)
  actual: (not (= [] :a0))

FAIL in (test-map-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:181)
Map patterns
expected: (= (let [x {:a 1, :b 1}] (match [x] [{}] :a0 [{:a _, :b _}] :a1 [{:a 1, :b 1}] :a2 :else [])) :a0)
  actual: (not (= [] :a0))

FAIL in (test-map-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:189)
Map patterns
expected: (= (let [x {:a 1, :b 1}] (match [x] [{:x nil, :y nil}] :a0 [{:a _, :b _}] :a1 [{:a 1, :b 1}] :a2 :else [])) :a1)
  actual: (not (= [] :a1))

FAIL in (test-map-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:197)
Map patterns
expected: (= (let [x {:a 1, :b 2}] (match [x] [({:a _, :b 2} :only [:a :b])] :a0 [{:a 1, :c _}] :a1 [{:c 3, :d _, :e 4}] :a2 :else [])) :a0)
  actual: (not (= [] :a0))

FAIL in (test-map-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:205)
Map patterns
expected: (= (let [x {:a 1, :b 2, :c 3}] (match [x] [({:a _, :b 2} :only [:a :b])] :a0 [{:a 1, :c _}] :a1 [{:c 3, :d _, :e 4}] :a2 :else [])) :a1)
  actual: (not (= [] :a1))

FAIL in (test-map-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:213)
Map patterns
expected: (= (let [x {:a 1, :b 2}] (match [x] [{:a a, :b b}] [:a0 a b] :else [])) [:a0 1 2])
  actual: (not (= [] [:a0 1 2]))

FAIL in (test-or-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:269)
Or patterns
expected: (= (let [x {:a 3} y nil z nil] (match [x y z] [{:a (:or 1 2)} _ _] :a0 [{:a (:or 3 4)} _ _] :a1 :else [])) :a1)
  actual: (not (= [] :a1))

FAIL in (test-else) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:357)
Test :else support
expected: (= (let [v {:a 1}] (match [v] [{:a a}] 1 :else 21)) 1)
  actual: (not (= 21 1))

FAIL in (test-edge-cases-4) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:556)
Edge cases 4
expected: (= (let [x {:a 1, :b 2, :c 10, :d 30}] (match [x] [({:a _, :b _, :c _, :d _} :only [:a :b :c :d])] :a-1 [({:a _, :b 2} :only [:a :b])] :a0 [{:a 1, :c _}] :a1 [{:c 3, :d _, :e 4}] :a2 :else [])) :a-1)
  actual: (not (= [] :a-1))

FAIL in (test-edge-cases-4) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:567)
Edge cases 4
expected: (and (= (let [m {:a 1}] (match [m] [({:a 1} :only [:a])] :a0 :else :a1)) :a0) (= (let [m {:a 1, :b 2}] (match [m] [({:a 1} :only [:a])] :a0 :else :a1)) :a1))
  actual: false

FAIL in (test-edge-cases-4) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:580)
Edge cases 4
expected: (= (let [m {:foo 1, "bar" 2}] (match [m] [{:foo 1, "bar" 2}] :a0 :else :a1)) :a0)
  actual: (not (= :a1 :a0))

FAIL in (test-app-patterns) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:902)
Function application patterns
expected: (= (match [m] [{:a (2 :<< inc), :b _}] :match1 [{:a (3 :<< inc), :b _}] :match2 :else :no-match) :match2)
  actual: (not (= :no-match :match2))

FAIL in (test-misc-tickets) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:918)
Misc. tickets
expected: (= (let [xqq {:cz 1, :dz 2}] (match [xqq] [{:z a, :zz b}] [:a0 a b] [{:cz a, :dz b}] [:a2 a b] :else [])) [:a2 1 2])
  actual: (not (= [] [:a2 1 2]))

FAIL in (test-misc-tickets) (/Users/me/code/work/nbb-logseq/test/core_match_test_runner.cljs:926)
Misc. tickets
expected: (= (let [xmm {:bz 2}] (match [xmm] [{:az a}] [:a0 a] [{:bz b}] [:a1 b] :else [])) [:a1 2])
  actual: (not (= [] [:a1 2]))

Ran 20 tests containing 91 assertions.
20 failures, 0 errors.

This is because (instance? ILookup {}) is failing in nbb.

oh no sorry about that, instance? isn't being used in cljs branch

I suspect this one fails:

(satisfies? ILookup ~ocr)

are you running with a fork of this library?

if so, you can try to add a function that checks the satisfies condition like this:

'satisfies-ILookup? #(satifies? ILookup %)
to the namespace config and then substitute that instead

then I bet some tests will start working!

I’m not running a fork of abbinare yet. I can try tomorrow. After I add that fn I can add a :org.babashka/nbb conditional to https://github.com/viebel/abbinare/blob/e1e4a3c9d083b495a02c080f86a05aa10275e6d0/src/main/clojure/cljs/core/match.cljc#L1277 . I’d be happily surprised if that’s the only ILookup call I have to change 🤞

I think you're confusing things. That source code won't be seen by nbb if you add it via SCI bindings

but you can however make this function as part of that lib and then it will work there as well

so just something like

(defn satisfies-ILookup? [x] (satisfies? ILookup x))
and then use this function in the macro-expansion I pointed at

you then have to change nothing about the SCI bindings I think

oh yes, you do need to add that function to the SCI bindings as well of course since it's used in the macro-expansion

anyway, in that case the lib will still work with SCI bindings and in regular CLJS

Tests all pass! Thanks for the guidance. https://github.com/logseq-cldwalker/abbinare/commit/1d435ea6b3c6269196c6f1b137af78790a8b767c is the abbinare change. https://github.com/logseq/nbb-logseq/tree/add-core-match adds the core.match bindings. One weird quick is that when I pulled in the latest nbb with the updated sci, building nbb-logseq fails with

Error building classpath. Unable to compare versions for org.babashka/sci: {:mvn/version "0.10.46", :deps/manifest :mvn} and {:git/url "", :git/sha "56104c81cb7a3804f8ee84aa64b313dc7143f30e", :deps/manifest :deps, :deps/root "/Users/me/.gitlibs/libs/org.babashka/sci/56104c81cb7a3804f8ee84aa64b313dc7143f30e"}
I wasn’t seeing this before when I depended on https://github.com/babashka/nbb/blob/9119dbe9b5f660cb77d29058b3b762978ee4b939/deps.edn which also depended on a git-based sci. Anyways, I’ll figure it out and look forward to using core.match in nbb next week

nice, congrats!

With chat-gpt’s help I was able to get https://github.com/logseq/nbb-logseq/blob/add-core-match/features/core.match/src/nbb/impl/core_match.cljs but it requires https://github.com/viebel/abbinare. Gonna have to poke at it more to see if it fully works

it's just a matter of fixing the macro expansion to use fully qualified vars I think?

I don't know if you have to fork core.match, but you need to copy the macros from clj to cljs

since those are normally executed in JVM Clojure

but perhaps that's just what viebel's version does, dunno