Since ClojureScript started officially supporting ECMAScript 2016 can we eventually expect an official support for JS classes in ClojureScript?
FWIW it was always possible to use and make JS classes in CLJS.
And if you use shadow-cljs, there's a more ergonomic defclass: https://clojureverse.org/t/modern-js-with-cljs-class-and-template-literals/7450
+1 on having classes AND template literals in cljs itself
@p-himik, I'm aware of shadow-cljs.modern/defclass, but despite its popularity, it's not official. And I don't know of any other way to create JS classes in CLJS.
And classes are sometimes more ergonomic than function prototypes/Object.create/setPrototypeOf/whatever because certain features in JS are designed with classes in mind (such as web components).
You can work around lack of classes in all cases I can think of, but then you have something like this https://gist.github.com/thheller/36332574918b974a3e4996efcb7457d2
And it's not that bad, but definitely using classes could make it more readable
> I don't know of any other way to create JS classes in CLJS Yes, I meant that manual prototype manipulation that you linked.
I'm not convinced that adding a bunch of syntax to support classes makes a whole lot of sense, Reflect supports just about everything except private members. There's no precedent for the general idea in Clojure either, i.e. Clojure doesn't really provide a fully general way to define classes
re: template literals, what about #js "foo"?
How would that look with placeholders for interpolation - same as in JS, e.g. #js "Hello ${name}"?
What about tag functions?
What about line breaks inside the string?
why are line breaks a problem?, do you mean that's not supported in JS, sorry I don't know anything about template literals in JS since it's not an interesting feature to me - but happy to learn / be convinced.
Oh, my bad - knowledge cross-pollination from other languages. I was under the impression that template literals remove leading whitespace, but it's not the case in JS. But it is in e.g. Java 15+.
the interesting bit about js templates is the tag and identity aspect. as in the tag function receives first strings array argument and that is guaranteed to be interned basically. so you can compute something based on it and use it as a cache key or whatever in a Map. some JS libs exploit that, but IMHO doesn't make too much sense in CLJS since we have macros to do more interesting stuff and replicated this if needed.
defclass I have used tons and wouldn't want to miss 🤷
Good examples of why you actually need it? It's never come up on any work project I've done, but of course that doesn't mean much. It just has never seeemed necessary to do anything.
I have only needed it once - to interoperate with JS code generated/supplied by ANTLR4:
(ns ...
(:require [shadow.cljs.modern :refer [defclass]]
["/...generated.../ABCMusicListener" :refer [ABCMusicListener]]
["antlr4/error/ErrorListener" :refer [ErrorListener]]))
(def ^boolean debug-parser? false)
(when debug-parser?
(defclass NotePrinter
(extends ABCMusicListener)
(constructor [this] (super))
;; TODO: Try to replace `Object` with `ABCMusicListener` when ANTLR
;; is upgraded past 4.9 so it generates proper classes.
Object
(enterNote [this ctx]
(console :info "NOTE" ctx))))
(defclass ABCParserErrorListener
(extends ErrorListener)
(field -error-atom)
(constructor [this error-atom]
(super)
(set! -error-atom error-atom))
Object
(syntaxError [this _recognizer _offending-symbol _line column msg _e]
(reset! -error-atom (str msg " (character " (inc column) ")"))))right, I'm not using bunch of libs that require inheritance and not because I'm trying to avoid them. But I could be out of touch here.
Is the new function really called, literally, "builder"? In Clojure's world of persistent data structures, every function is a "builder" of one kind or another. Many of them are distinguished by being called after what gets built, e.g., fn, hash-map, set, proxy, etc. "builder" having a namespace all-to-itself is a technicality, because the namespace vanishes when as illustrated in the announcement the function is typically referred. Of course, rule No.1 is that naming is hard, so we maintain discussion on an elevated plane by not obsessing over it. I will compromise by not obsessing over what the right name might be 🙂 The squad has come up with about 1000 good names so far. The 1001st should be a walk in the park!
> the namespace vanishes when as illustrated in the announcement the function is typically referred
I wouldn't treat code in announcements as an endorsement of particular approaches. :) I myself almost never use :refer, IMO it's just not worth in in 99% of all cases. So I'd instead require e.g. [cljs.proxy :as proxy]. Or maybe even just [cljs.proxy].
Also if folks have better names in mind now's the time to suggest something better before we get stuck with something we don't like.
very open to better names for cljs.proxy/builder before it gets set in stone - I didn't love cljs.proxy/proxy because that's inaccurate, as well as if we do use it that might just be for the default provided proxy. Also maybe that's the actual complaint, I expect builder to be less-common, so maybe the name is fine.
If I had to give a name to that function, I'd go with factory .
I actually like builder. Same meaning as factory, one syllable fewer. Less enterprisey. :)
But also, I imagine that 95% of all usages will be exactly like it's described in the announcement - the 0-arity of (builder) would get used.
So, maybe it makes sense to also add something like (def keyword-proxy (builder)) to cljs.proxy?
I can't help but imagine keyword-proxy is omitted by design, it's too much of an elephant in the room. Perhaps the intention is for each lib to use their own builder so as to minimize cache contention.
At first I wanted to ask what you meant exactly by "cache contention", but now I see that
(when (== cnt 1024)
(.clear this))
Right...
This to me feels significantly more interesting than bickering over whether builder is aptly named.
If the cache size were parameterizable, I'd 100% set it to ##Inf in my apps' code - in my apps, I control everything, I don't dynamically create keywords left and right, and I know every single keyword that gets used.do we really need a builder intermediate? could it have worked with just
(proxy {:a 1})
as a drop-in replacement for clj->js ?That has the opposite problem to contention over the same cache. :)
If you use (proxy {:a 1}) 1000 times, you'll call (keyword "a") 1000 times.
You could have a pluggable cache. Could default to WeakMap. But maybe not ideal. I guess the builder makes sense if you want to make the cache pluggable without having to write your own wrapper for plugging in a custom cache.
> You could have a pluggable cache
That would be my preference.
> Could default to WeakMap
How? WeakMap cannot have string keys.
I think the bounded size cache is sensible, not everybody is as deliberate
> How? WeakMap cannot have string keys.
That's a good point
> I think the bounded size cache is sensible, not everybody is as deliberate Configurability does not preclude sensible defaults.
but if you did have a pluggable cache then the api could be something like
(defn keyfn-cache [bound keyfn] ...)
(defn proxy [keyfn-cache obj] ...)
with proxy defined such that (builder) = (partial proxy (keyfn-cache 1024 keyword)) (unless I'm missing some JS ctor weirdness)that makes sense to me
Having some trouble setting up webpack with shadow cljs This is my shadow-cljs.edn file
{:deps true
:builds {:frontend {:target :browser
:output-dir "public/js"
:modules {:main {:init-fn }}
:js-options {:js-provider :external
:external-index "target/index.js"}}}}
I've used webpack cli to setup webpack with the entry as entry: './target/index.js', and the output to the appropriate place. I've made sure that my html includes the webpack libraries first.
However I'm getting these errors:Update: I got it working! I had a defer in the script to include the libs but not a defer in the other which means the order would be changed. always the simplest fixes for the problems that keep you stuck for an hour+ lol