Fork me on GitHub
#cherry
<
2022-08-30
>
borkdude15:08:40

So one idea would be to emit vars as var foo_fn = {val: function() { } } so vars are always mutable, also from the outside (ES6-wise). calls to vars would be compiled as foo_fn.val(1, 2, 3) (a var deref, like in Clojure JVM). This comes with a little bit of performance overhead, but maybe not that much. This will solve the REPL and dynamic var problem I think

didibus16:08:37

If there's support for direct linking, then I'd assume any slowdown or extra garbage for var indirection wouldn't be a real issue, since it'll only manifest in dev

borkdude15:08:37

The annoying bit is when calling these suckers from JS: you also have to do the .val indirection, which sucks. Darn.

borkdude15:08:26

Or, what if you had:

> var foo_fn = function(...args) { return foo_fn.val(...args) }
> foo_fn.val = function(...args) { return [...args] }
> foo_fn(1, 2, 3)
[ 1, 2, 3 ]

> foo_fn.val = function(...args) { return args[0] }
[Function (anonymous)]
> foo_fn(1, 2, 3)
1

borkdude15:08:43

This only works for functions, and would create more garbage than necessary...

didibus17:08:42

Can someone educate me on the issue with ES6 for vars? I thought if you export a class instance, you get the same instance everywhere you import it, and then you can just mutate all it's fields from there, and it'll reflect everywhere. Doesn't that just mean that each namespace exports a class instance of the namespace name with each var a field on it? And then you could make an additional indirection again if we wanted to make it more like Clojure ?

borkdude17:08:22

Yes, that's one of the options (or just one global object for all namespaces) - the issue with this is that it doesn't really work for treeshaking. So you could do it in dev mode only. for example

borkdude17:08:31

But dynamic vars should always remain mutable, so maybe a per namespace mutable object where those live could work

didibus17:08:48

I'm no JS expert, but for some reason this one makes the most sense to me, also seems the most Clojure like, in how each namespace has a map of symbols -> vars and vars -> value

didibus17:08:26

The per-namespace mutable object is like the ns map, and then an actual Var object could be introduced which is a mutable container with support for dynamic push/pop bindings and a root value. And direct linking compilation would just get rid of the Var indirection and have the per-namespace mutable object just point to the value directly.

borkdude17:08:55

The trouble is that having all "vars" in a mutable object, doens't work with treeshaking (esbuild, etc)

didibus17:08:38

Hum, really, it can't see that a property of the object is never accessed and remove it?

borkdude17:08:23

according to esbuild's author it's not safe to make assumptions about that - GCC thinks otherwise

borkdude17:08:57

or actually, I'm not sure if GCC prunes those either: I don't think so

didibus17:08:01

Well, I guess direct linking could go a step further, and move those into their own exported var. You would still want the mutable object though in case people do dynamic defs no?

borkdude17:08:14

since this is what SCI relies on, a map with symbols to functions, those are never cut away

didibus17:08:47

Ya, I can see the idea, they don't know if the property will be added back later or not. I just thought maybe since those props are defined at instantiation, it would assume that if they were defined but not used, they won't ever be accessed dynamically. But I guess it thinks maybe you will.

didibus17:08:17

What if they were const instead of var? You would not be able to ns-unmap them, but it would still work if there was an additional Var indirection.

borkdude17:08:59

var or const doesn't really matter for treeshaking I think

borkdude17:08:20

a const {} is still mutable

borkdude17:08:58

it just means the const cannot be re-assigned

didibus17:08:12

The reference isn't though, but ya, maybe it doesn't matter. I guess it depends what their rationale is for not treeshaking properties

borkdude17:08:57

It just sucks that you have to emit different code for dev and prod

borkdude17:08:01

and dynamic vars

didibus17:08:21

> Sometimes it's desirable to allow some code to be tree shaken even if that code can't be automatically determined to have no side effects. This can be done with a https://esbuild.github.io/api/#pure which tells esbuild to trust the author of the code that there are no side effects within the annotated code. The annotation comment is /* @__PURE__ */ and can only precede a new or call expression. Could this annotation force them to be treeshaken?

didibus17:08:20

Ya, I feel ya haha. At least I do feel like turning direct-linking on is common in standard Clojure as well for prod builds, and has a similar effect that you know you cannot redef those vars in the prod build.

didibus17:08:06

Hum, ok I'm not following everything, but it seems some treeshaker like Terser and Rollup shake static properties. Could static properties be used then?

borkdude17:08:22

What are static properties?

didibus17:08:27

It seems ES6 classes support static properties, similar to static in Java, they're variables set on the class itself and not on an instance of a class.

didibus17:08:04

And it looks like Rollup and Terser can treeshake them, but webpack might not, but then it seems if you set them with an IIFE and add the PURE annotation it will treeshake them

borkdude17:08:14

I don't see why we would use classes

didibus17:08:04

Something like this:

var NsMap = /*#__PURE__*/function () {
  var NsMap = /*#__PURE__*/function NsMap() {
    _classCallCheck(this, NsMap);
  }
  NsMap.someVar = a-var;
  NsMap.someOtherVar = another-var;
  return NsMap;
};

export default NsMap;
Now I think you can access NsMap.someVar from other places, and it would be treeshaken if not used.

didibus18:08:03

Not really sure though, maybe it doesn't work conssistently.

didibus18:08:26

Your proposal to make vars functions might be best honestly, considering the treeshaking. I suspect bundle size is generally more important than whatever slower/more garbage is created.