Fork me on GitHub
#cljs-dev
<
2022-01-27
>
thheller07:01:32

on the clojure.math naming issue? Should we not start with cljs.math from the outset? We have automatic aliasing for clojure.* -> cljs.* so clojure.math will still work? When using clojure/math.cljs we eternally have a namespace that cannot ever have macros since clojure/math.clj already exists. It doesn't have macros yet but there might be a point where we want to? even if we just use the macros to do some inlining?

quoll12:01:43

I could see the benefit in changing the simple wrapping functions, e.g.

(defn pow
 [a b]
 (js* "Math.pow((~{}), (~{}))" a b))
I note that this doesn’t need a macro, though from what I can see, functions that use js* are typically defined in the cljs.* namespaces instead of the clojure.* ones. I’m OK with this change, but I’d like to defer to people like David who have a better sense of where things should go.

borkdude12:01:42

Why does clojure.math prevent using macros? You could place the macros elsewhere and load them from there?

☝️ 1
dnolen13:01:25

ah that's true about inlining macros - @quoll what @thheller is talking about is avoiding a classpath clash - I think that is reasonable

dnolen13:01:36

people can still use clojure.math

dnolen13:01:48

@borkdude the problem is you need the runtime file and the macros file to have the same name for the ClojureScript compiler to automatically load the macros for consumers of the library

dnolen13:01:12

to avoid :require-macros and :require for what is effectively the same library

borkdude13:01:01

then for clojure.string shouldn't that also have happened? There might be similar optimizations to be made there using macros

thheller13:01:13

yeah, the same applies there but it predates the pattern of using the self-requiring :require-macros pattern to make macro use "prettier"

👍 1
quoll13:01:32

Sorry, when I talked about the js* form I was thinking about this as the inlining mechanism in macros, and that it would have to be in another file. From what I see here (and seeing how namespaces like core work), then what I think is consistent is to have inlining macros in, say, src/main/clojure/cljs/math.cljc, and that these can be loaded with the existing clojure.math? Is that right? If so, then is it OK that the function-based file stays where it is?

thheller14:01:59

js* is not for inlining. (math/pow 1 2) will still end up as a call to (cljs.math/pow 1 2). you could change the impl to (defn pow [a b] (js/Math.pow a b)) and it would be identical to the js*. the inlining is done by having a macro of the same name that just changes the output to bypass the call to cljs.math/pow

borkdude14:01:24

@quoll You're probably aware of this, but if not: CLJS has the concept of macro-fns: a call to a function can go through a macro first, if it's called in function position, while in non-function position it would always fall back on the function implementation. More info here: https://blog.fikesfarm.com/posts/2015-12-12-clojurescript-macro-functions.html That allows you to do optimizations, while always being able to refer to the function value as a normal function as well.

dnolen15:01:49

@quoll inlining macros are not necessarily about js*

quoll15:01:32

OK, I have my head on with fewer distractions now. Forgetting what I was saying earlier… would it be useful to create a macro like:

(defmacro pow [a b] (list 'Math/pow a b))
? Right now, if something uses (clojure.math/pow a b) in a function it gets converted into:
cljs.math.pow.call(null,a,b)
which isn’t ideal

thheller16:01:07

thats only true for non-optimized builds. :advanced builds will have :static-fns true. that'll eliminate the .call. :advanced will also very likely take care of cljs.math.pow and just end up replacing it with the inlined Math.pow directly

favila16:01:39

I’ve always looked at advanced compilation output to decide

favila16:01:34

it’s hard to predict what it can eliminate though; if I see it can’t produce good enough code i’ll make a macro to emit code it could do a better job with

favila16:01:46

IOW a pow macro like you suggest will probably more predictably and consistently produce faster js, but may not actually be necessary to do so, and you lose the ability to redef that var reliably in e.g. a repl (non-advanced compilation) context.

dnolen16:01:06

in the first iteration of the loop i=0, j=0 so equalKey is true - then you take the slow path

Alex Miller (Clojure team)16:01:10

yes, it's checking for duplicate key, per the docstring

dnolen16:01:39

but it always take the slow path

dnolen16:01:46

its looking at index=0

dnolen16:01:24

ah, good - thought I was misreading

dnolen17:01:14

@fogus @alexmiller thanks! getting close

dnolen17:01:53

porting this kind of code over is always an exercise 🙂

fogus (Clojure Team)17:01:52

this one is pretty subtle too IMO

Alex Miller (Clojure team)17:01:27

good as a review for clj side too!!

dnolen18:01:08

@quoll left a couple of comments in JIRA

👍 1