clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
nilern 2021-03-19T08:33:32.055500Z

@lilactown await would just make the surrounding function return a Promise and you would still have the same problem?

nilern 2021-03-19T08:42:33.059100Z

Could you just shim cljs.test/async on the clj side or something:

(defmacro async [done & body] `@(do ~@body))
Maybe there should be a proper implementation in clojure.test though

zendevil 2021-03-19T12:30:46.060600Z

Has anyone tried the http://hamsters.io library for multithreading js? Would you recommend it, or suggest alternatives?

zendevil 2021-03-19T12:33:22.061400Z

Would it be recommended to be used in a e.g. react application to boost performance?

p-himik 2021-03-19T12:39:11.061500Z

The only possible recommendation with such a vague description could only be to first measure what exactly degrades the performance. I.e. browser profiling. Only then you can start think of proper solutions.

p-himik 2021-03-19T12:40:39.061700Z

Also, in browsers there's no backend-like multithreading. Regardless of what library you choose, it will use web workers. Web workers have a bunch of limitations so it's actually very likely that you will see performance degradation and not improvement when using such a library for some scenarios.

dnolen 2021-03-19T12:55:34.062300Z

@ps I am skeptical it would make much of a difference

2021-03-19T12:59:34.063700Z

for curious, I've made compiler extension a while ago that adds async/await specials https://github.com/roman01la/cljs-async-await not something one would want to use of course, also doesn't remove potential breakage because of intermediate IIFEs generated by the compiler

1
2021-03-19T13:05:25.068700Z

I think there must be something I’m not understanding about the code splitting feature in cljs. I have two builds sharing much of the same code. After optimization, build A is 900k and build B is 800k. I introduced modules, and now mA is 80k, mB is 40k, but cljs_base is 1080k. How can the shared code (intersection of) A and B be larger than either?

2021-03-19T13:06:36.069500Z

Maybe closure isn’t able to do a proper tree shaking?

thheller 2021-03-19T13:09:27.072700Z

it may be larger because the module loader adds a bit of code you otherwise might not have used

nilern 2021-03-19T13:09:39.073100Z

You could have some code that is called once from A and once from B and can be inlined and then shrunken by optimizations in the separate builds https://www.cs.princeton.edu/~appel/papers/shrink.pdf

thheller 2021-03-19T13:09:53.073200Z

tree shaking and cross-module-montion largely depends on how the code is written

2021-03-19T13:10:22.073700Z

I see. Thanks for the quick responses!

2021-03-19T13:13:42.075200Z

Is there hope that I might see different/better results with shadow-cljs, @thheller, or is it the same underlying mechanism at play?

thheller 2021-03-19T13:15:35.076800Z

impossible to say without knowing what your code does. results will vary if the modules use different npm packages. if its purely CLJS code there likely won't be a big difference

👍 1
thheller 2021-03-19T13:17:19.077400Z

but you will get build reports that will give you a much better picture about what is in your modules 😉 https://shadow-cljs.github.io/docs/UsersGuide.html#build-report

👏 1
2021-03-19T13:22:03.078400Z

Now that looks interesting. I’ll give it a shot. Thanks!

dnolen 2021-03-19T13:36:34.079400Z

@magnars no matter what you do have to build an understanding of how code splitting works to get the best results

dnolen 2021-03-19T13:36:55.080Z

it's not a magical thing - it works best if you have plan around your dependencies and you know where you don't want the heavy stuff to load

dnolen 2021-03-19T13:37:19.080400Z

just turning it on isn't going to offer much

dnolen 2021-03-19T13:37:52.080900Z

for example you have a React app, but you don't need that for login

dnolen 2021-03-19T13:38:08.081500Z

then your login page <30K gzipped

dnolen 2021-03-19T13:39:45.082100Z

in general if I'm building something simple, ClojureScript and basic Google Closure Library is all you need

dnolen 2021-03-19T13:40:22.082900Z

and the payload is always around jQuery size

dnolen 2021-03-19T13:40:33.083400Z

but this is the way to think about code splitting

dnolen 2021-03-19T13:40:42.084100Z

what pages need lots of dependencies and which don't

2021-03-19T13:42:02.085900Z

Good points, for sure. I guess I was hoping it would be just a little magical. 🙃 What surprised me was that “extracting common code” would result in more code than either source.

dnolen 2021-03-19T13:43:17.086700Z

the loader definitely brings in some machinery

dnolen 2021-03-19T13:43:45.087400Z

code splitting for Google Closure stuff is very good

dnolen 2021-03-19T13:43:51.087600Z

it will move properties and functions

dnolen 2021-03-19T13:44:11.088100Z

but if your dependency graph wasn't planned for the split, nothing interesting is going to happen

dnolen 2021-03-19T13:44:30.088400Z

so visual tools are nice here

dnolen 2021-03-19T13:45:05.089100Z

but starting out with an actual plan is better - the results are quite obvious if you manage your deps and play around w/ it for a bit

dnolen 2021-03-19T13:45:55.090200Z

you do have be careful w/ JS random libraries

dnolen 2021-03-19T13:46:04.090600Z

all that can be done is coarse grained moves

dnolen 2021-03-19T13:46:10.091Z

so you're just pushing boulders around

2021-03-19T13:46:44.092600Z

Thanks, that’s helpful. 👍 It’s good to know that I can get better results by reorganizing my code to better align with how the code splitter works.

dnolen 2021-03-19T13:46:47.092800Z

for Closure stuff and ClojureScript stuff it can broken apart, but you also have to realize common ClojureScript stuff is always gonna end up in the base

dnolen 2021-03-19T13:48:32.094400Z

but even here tricks are possible

dnolen 2021-03-19T13:48:56.094900Z

i.e. login page written ClojureScript but you take care to only use JS objects and arrays

dnolen 2021-03-19T13:49:01.095100Z

then login page will be bytes

dnolen 2021-03-19T13:50:02.095900Z

the hello-world compile test size we have in ClojureScript which seems artificial is useful for confirming that this style of planning can be done

2021-03-19T13:50:46.096500Z

Interesting!

dnolen 2021-03-19T13:51:59.096900Z

I tried all this stuff when I originally integrated w/ the Closure code splitter

dnolen 2021-03-19T13:52:18.097400Z

so it's not magical, but if you understand it - it can be wielded w/ surgical precision

dnolen 2021-03-19T13:53:23.098100Z

I would not bother trying this stuff w/ JS tools, maybe I'm wrong

dnolen 2021-03-19T13:53:32.098600Z

but the dep graphs make this type of thing too hard to reason about

2021-03-19T13:54:31.100200Z

Any reading material on the topic to suggest? I guess I can use thheller’s build report to find some culprits and experiment from there.

dnolen 2021-03-19T13:54:42.100400Z

not much reading material

dnolen 2021-03-19T13:54:53.100700Z

but I would say just play it with it for a couple of days

dnolen 2021-03-19T13:54:57.100900Z

and do what I said above

dnolen 2021-03-19T13:55:15.101300Z

try doing a login page w/ ClojureScript and no deps, see the output size

dnolen 2021-03-19T13:55:25.101600Z

do an inner page w/ React etc.

dnolen 2021-03-19T13:55:40.102Z

not a real app but trivial examples that demonstrate it can be done

dnolen 2021-03-19T13:55:57.102400Z

and after that it will be come quite obvious how to do it

2021-03-19T13:56:36.103Z

Excellent. Thanks for taking the time to explain. Much appreciated.

dnolen 2021-03-19T13:56:58.103200Z

bug reports welcome of course too 🙂

🙂 1
dnolen 2021-03-19T13:59:25.103800Z

@magnars I see you commented on the loader regression, will take a look at that later

👍 1
dnolen 2021-03-19T14:01:18.104Z

https://clojure.atlassian.net/browse/CLJS-3300

2021-03-19T14:06:25.106500Z

Thanks for the link! I’ll add it to the GitHub issue for those who wondered where to look (like me). 👍

2021-03-19T15:31:37.111500Z

To summarize what I understood today: Given two builds A and B, in a perfect world the size of (module A + base) = size of A, and the size of (module B + base) = size of B. However, we don’t have a perfect (magical) code splitter. To get a good split, the code needs to be organized (via namespaces) along the same lines as the modules. Better alignment yields better splits.

thheller 2021-03-19T15:36:46.112600Z

technically closure is able to split namespaces as well and move things wherever they are needed but certain things prevent that from working (eg. defmethod can't be moved)

👍 1
thheller 2021-03-19T15:39:33.114500Z

but yeah carefully designed code and namespaces split better in practice

lilactown 2021-03-19T17:23:27.116500Z

yeah that maybe would help

lilactown 2021-03-19T17:59:06.116900Z

I would need to detect whether I'm in a CLJS or CLJ context when emitting code, since macros are defined in Clojure. Or continue to split my CLJS and CLJ test files

nilern 2021-03-19T18:10:22.117100Z

Yes I thought that would be an issue but you can actually do (:require #?(:clj [my.async-shim :refer [async]]) in the tests

fsd 2021-03-19T18:43:40.122Z

Hi There, I had a quick question, I am trying to disable a HTML button using ClojureScript

(def zoom-in-button  (-&gt; js/document (.getElementsByClassName "mapboxgl-ctrl-zoom-in")))

 (if (= max-zoom (:zoom viewport))
    ((first zoom-in-button)(.setAttribute "disabled" "disabled")))
Trying to do
&lt;button class="mapboxgl-ctrl-zoom-in" type="button" title="Zoom In" disabled =""&gt;&lt;/button&gt;
But keep on getting this error Uncaught TypeError: "disable".setAttribute is not a function In JavaScript
let ZoomIn = document.getElementsByClassName("mapboxgl-ctrl-zoom-in")[0];
ZoomIn.setAttribute("disabled","disabled")

lilactown 2021-03-19T18:47:22.123900Z

((first zoom-in-button)(.setAttribute "disabled" "disabled")))
this is the equivalent JS:
ZoomIn[0]("disabled".setAttribute("disabled"))

lilactown 2021-03-19T18:47:32.124100Z

do you see the problem?

fsd 2021-03-19T18:53:09.124200Z

Ahh it’s calling .setAttribute on array element :thinking_face: Am I right ?

lilactown 2021-03-19T18:53:43.124400Z

no, it's calling .setAttribute on the string "disabled", like your error message says

lilactown 2021-03-19T18:54:33.124600Z

if we break it down piece by piece,

(.setAttribute "disabled" "disabled")
literally translates to JS:
"disabled".setAttribute("disabled")

lilactown 2021-03-19T18:54:54.124800Z

which is throwing the error you see

lilactown 2021-03-19T18:56:28.125Z

you want to call .setAttribute on the zoom-in-button, not the "disabled" string

lilactown 2021-03-19T18:57:13.125200Z

the .method syntax in ClojureScript calls the method on the first argument

lilactown 2021-03-19T18:57:27.125400Z

(.setAttribute x "disabled" "disabled")

fsd 2021-03-19T18:58:19.125600Z

I am kinda new in ClojureScript and trying to wrap my head around this

fsd 2021-03-19T19:01:28.125800Z

would you please give me little help how I can call on .setAttribute the element ?

lilactown 2021-03-19T19:03:38.126Z

CLJS:

(.setAttribute button "disabled" "disabled")
will be the same as JS:
button.setAttribute("disabled", "disabled")
does that help?

fsd 2021-03-19T19:06:00.126200Z

Ohhh I get it Thank you @lilactown

lilactown 2021-03-19T19:06:58.126400Z

yw!