clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
sh54 2021-06-09T12:50:16.158600Z

I was looking into porting over https://github.com/stuartsierra/clojure.walk2 to clojurescript. My initial tests show a 2-5x speedup. But I am having some issues dispatching records correctly. There are certainly some clojure/script nuances around protocols that I am not up to speed on. So here is a little clojure to clojurescript comparison getting at the crux of the matter: (this example code is just mimicking a portion of the walk2 code. Clojure

(defprotocol SomeP
  (report [coll]))

(extend-protocol SomeP
  java.lang.Object
  (report [x]
    (println :SomeP :path :object :is-record? (record? x)))
  clojure.lang.IRecord
  (report [x]
    (println :SomeP :path :record)))

(extend-type clojure.lang.PersistentArrayMap
  SomeP
  (report [x]
    (println :SomeP :path :map)))

(defrecord Foo [a b c])

(report 1234)
;; :SomeP :path :object :is-record? false

(report {:a 1})
;; :SomeP :path :map

(report (map->Foo {:a 1 :b 2 :c 3 :extra 4}))
;; :SomeP :path :record
Clojurescript
(defprotocol SomeP
  (report [coll]))

(extend-protocol SomeP
  object
  (report [x]
    (println :SomeP :path :object :is-record? (record? x)))
  number
  (report [x]
    (println :SomeP :path :number))
  cljs.core.IRecord
  (report [x]
    (println :SomeP :path :record)))

(extend-type cljs.core.PersistentArrayMap
  SomeP
  (report [x]
    (println :SomeP :path :map)))

(defrecord Foo [a b c])

(report 1234)
;; :SomeP :path :number

(report {:a 1})
;; :SomeP :path :map

(report (map->Foo {:a 1 :b 2 :c 3 :extra 4}))
;; :SomeP :path :object :is-record? true
Without the extend on object then (report (map->Foo {...})) fails so the extend on cljs.core.IRecord does nothing. So my question is in clojurescript can I use extend-protocol to work on any record type? I can always just check with record? when the record gets dispatched as an object but I was hoping to avoid that.

dnolen 2021-06-09T13:21:38.159400Z

@slack1003 it cannot be avoided - you cannot extend a protocol w/ a protocol which is what you're trying to do

dnolen 2021-06-09T13:22:12.159800Z

in Clojure this works due to protocols being mapped to Java interfaces

sh54 2021-06-09T13:33:18.160300Z

k that makes sense. Thanks for the info!

jhacks 2021-06-09T15:17:13.162200Z

Is it possible to test for whether an object is a JavaScript object or a ClojureScript object? I would like to test when something is going to print as an #object at the REPL, for example:

<http://demo.app|demo.app>&gt; (-&gt; js/document (.getElementById "63R"))
    #object[HTMLSelectElement [object HTMLSelectElement]]
I tried instance?, but that doesn’t distinguish ClojureScript objects:
<http://demo.app|demo.app>&gt; (instance? js/Object. (-&gt; js/document (.getElementById "1120R")))
    true

    <http://demo.app|demo.app>&gt; (instance? js/Object. {})
    true
I tried object?, but that doesn’t work:
<http://demo.app|demo.app>&gt; (object? (-&gt; js/document (.getElementById "1120R")))
    false

dnolen 2021-06-09T15:19:31.162600Z

there's a low level thing cljs$lang$type boolean field

thheller 2021-06-09T15:22:15.163800Z

technically you can also extend the printer protocols on JS types, so even though it may not be a CLJS type that doesn't mean it'll guaranteed print as #object

jhacks 2021-06-09T15:30:42.164600Z

Forgive my ignorance, but how do I check for the field?

<http://demo.app|demo.app>&gt; (.-cljs$lang$type js/Object.)
nil
<http://demo.app|demo.app>&gt; (.-cljs$lang$type {})
nil

borkdude 2021-06-09T15:33:37.164900Z

@jhacks

cljs.user=&gt; (defrecord Foo [])
cljs.user/Foo
cljs.user=&gt; (.-cljs$lang$type Foo)
true

jhacks 2021-06-09T15:37:50.165700Z

Ah, I need to use type with it. Thanks @dnolen, @thheller, and @borkdude!

<http://demo.app|demo.app>&gt; (.-cljs$lang$type (type js/Object.))
nil

<http://demo.app|demo.app>&gt; (.-cljs$lang$type (type {}))
true