cherry

borkdude 2022-08-30T15:36:40.407629Z

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

2022-08-30T16:52:37.316319Z

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

borkdude 2022-08-30T15:38:37.074329Z

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

borkdude 2022-08-30T15:40:26.289699Z

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

borkdude 2022-08-30T15:43:43.796079Z

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

2022-08-30T17:11:42.007409Z

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 ?

borkdude 2022-08-30T17:14:22.193779Z

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

borkdude 2022-08-30T17:17:31.382609Z

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

2022-08-30T17:21:48.252209Z

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

2022-08-30T17:25:26.773379Z

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.

borkdude 2022-08-30T17:26:55.310339Z

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

2022-08-30T17:27:38.790369Z

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

borkdude 2022-08-30T17:28:23.602399Z

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

borkdude 2022-08-30T17:29:57.828839Z

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

2022-08-30T17:30:01.648179Z

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?

borkdude 2022-08-30T17:30:14.291289Z

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

2022-08-30T17:31:47.700289Z

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.

2022-08-30T17:35:17.765959Z

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.

borkdude 2022-08-30T17:35:59.596759Z

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

borkdude 2022-08-30T17:36:20.905909Z

a const {} is still mutable

borkdude 2022-08-30T17:36:58.526029Z

it just means the const cannot be re-assigned

2022-08-30T17:37:12.861709Z

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

borkdude 2022-08-30T17:38:57.920849Z

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

borkdude 2022-08-30T17:39:01.619129Z

and dynamic vars

2022-08-30T17:39:21.615409Z

> 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?

2022-08-30T17:40:20.952669Z

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.

2022-08-30T17:52:06.345469Z

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?

borkdude 2022-08-30T17:54:22.825579Z

What are static properties?

2022-08-30T17:55:27.187009Z

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.

2022-08-30T17:56:04.914719Z

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

borkdude 2022-08-30T17:56:14.281979Z

I don't see why we would use classes

2022-08-30T17:59:04.499319Z

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.

2022-08-30T18:03:03.502249Z

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

2022-08-30T18:03:26.851689Z

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.