Fork me on GitHub
#squint
<
2022-08-15
>
Cora (she/her)01:08:43

ok, my PR is ready for review

Cora (she/her)01:08:05

also: it may be worth adding a prettierjs config to the project and to use that for formatting code.

lilactown03:08:17

provocative question: should we be defining conj/assoc/get/etc. as protocols instead?

Cora (she/her)03:08:09

I mean it would sure simplify some things

Cora (she/her)03:08:32

I didn't think that was an option here

Cora (she/her)03:08:19

I pushed up some extras on that PR

lilactown03:08:59

protocols don't work yet

lilactown03:08:23

and it generates a LOT of code

Cora (she/her)03:08:38

it would be so nice if we could extend core types

lilactown03:08:21

yeah I'm thinking that we could keep the existing impls and add a protocol that it dispatches for custom types

lilactown03:08:58

the "don't extend built in types" JS thing makes all this business a real PITA

Cora (she/her)03:08:26

we could do like some libraries and have a wrap/unwrap thing

Cora (she/her)03:08:34

where we wrap values with our own objects for the duration of the function in order to simplify the impl

Cora (she/her)03:08:49

it doesn't solve the whole custom type dispatch issue

Cora (she/her)03:08:06

like I have this:

Cora (she/her)03:08:07

if (m instanceof Map) {
    m.set(k, v);

    for (let i = 0; i < kvs.length; i += 2) {
      m.set(kvs[i], kvs[i + 1]);
    }
  } else if (m instanceof Object) {
    m[k] = v;

    for (let i = 0; i < kvs.length; i += 2) {
      m[kvs[i]] = kvs[i + 1];
    }

Cora (she/her)03:08:21

sure would be nice to not have to do that

lilactown03:08:58

if it weren't for the fact we can't extend built ins, then a protocol could just be:

(defprotocol IConj
  (conj [o x]))
let IConj_conj = Symbol.for("clavascript.core.IConj/conj");

function conj(o, x) {
  let m = o[IConj_conj];
  if (!m) throw new Error("Object does not implement conj");
  return m(o, x);
}

Cora (she/her)03:08:01

it still extends core types but it does it in a way that cannot conflict with other things

Cora (she/her)03:08:30

which you have there

lilactown03:08:45

yeah that's p similar

lilactown03:08:08

I think the argument against this is that it's possible that a JS environment could freeze the built in types

lilactown03:08:21

to prevent prototype pollution attacks

lilactown03:08:40

I have never seen this in practice

lilactown03:08:17

I really wish TC39 would address this use case officially

Cora (she/her)03:08:10

> Map[Symbol.hasInstance](new Map())
true
> Map[Symbol.hasInstance]([])
false
> Map[Symbol.hasInstance]({})
false

Cora (she/her)03:08:24

turns out this is a built-in example of this working

Cora (she/her)03:08:36

but you're right that they could freeze things

Cora (she/her)03:08:28

ah, Symbol.species is another

Cora (she/her)03:08:35

I always wondered what that was about on MDN

Cora (she/her)04:08:06

it's function by function so not quite what we need but

borkdude07:08:30

About the above: I don't think we have to cover CLJS behavior 100%. E.g. (conj nil ...) could just not be supported: our contract is that conj works on arrays, objects and Maps, nothing else for example. I prefer good and fast support for built-in types rather than making things more complicated and slower to support conj on custom types.

borkdude07:08:48

Also, I think I'd prefer writing some gnarly boilerplate if that means we can have pretty CLJS code which compiles to fast and compact JS.

borkdude07:08:22

That said, protocols do already work:

(ns foo (:require ["clavascript/core.js" :refer [nil_QMARK_ not PROTOCOL_SENTINEL]]))

(defprotocol IFoo (foo [_]))
(deftype Foo [x] IFoo (foo [_] :foo))
(js/alert (foo (->Foo 1)))

borkdude07:08:54

There's just an issue with the automatic imports not properly working for some reason which is why you need the clavascript/core.js import now

borkdude08:08:07

About prettier: why is let res = f(ctr,x); better than let res = f(ctr, x);?

borkdude08:08:44

And return n+1; is changed into return n + 1, where is the consistency in that?

borkdude08:08:21

I prefer to work in small PRs that don't add a ton of stuff in one go, so it's easier to review

borkdude08:08:28

I'll add a PR template for this

borkdude08:08:29

E.g. about assoc-in we have a separate issue and I think we should first discuss if assoc-in should make clones of each object, or update them in place. This is an important decision that needs some thought instead of slipping it through in a PR I think

borkdude09:08:28

What I wonder about the above changes:

borkdude09:08:59

would it be faster if you check .length before you go into a for (const x = ...) loop? This is what was done before in assoc Also I wonder if writing for (x = 0; x < ..; x++) ... is faster than for (const x = ...)

borkdude09:08:19

I think we can safely say that using the imperative counter is way faster: https://jsbench.me/cbl6ujmp3d/1

1
borkdude09:08:21

@corasaurus-hex @lilactown I appreciate your involvement in this project a lot

💜 2
borkdude13:08:10

How do you call the clavascript community? The enclava :drum_with_drumsticks:

😂 1
borkdude14:08:11

What's the clava compiler invoked from bb called? baclava :drum_with_drumsticks:

1
borkdude14:08:57

So this defprotocol example works in the https://clavascript.github.io/clavascript/:

(defprotocol IFoo (foo [_]))
(deftype Foo [x] IFoo (foo [_] :foo))
(js/alert (foo (->Foo 1)))

borkdude14:08:34

Also this works:

(extend-type string IFoo (foo [_] :dude))
(js/alert (foo "dude"))

borkdude16:08:21

drat, enclava is already taken as an org name on github ;)

Cora (she/her)16:08:59

so do you think we should be building core functions in terms of protocols?

borkdude16:08:08

well, I think we should focus on performance first and start supporting the basic data structures JS has: object, array, Map and Set

borkdude16:08:20

and then it if turns out to be very useful, we could introduce protocol for assoc, etc

borkdude16:08:32

which then dispatches on the user implementation for the rest of the cases

borkdude16:08:16

unless you can convince me that a protocol dispatch on object and array is very cheap with a benchmark :)

Cora (she/her)16:08:37

there's just no way protocols could be cheaper than going with direct and imperative code

Cora (she/her)16:08:52

so I'll stay the course :)

borkdude16:08:06

indeed. so I think we should have direct imperative code for the most common cases and then have a fallback on a theoretical future protocol

borkdude18:08:47

@corasaurus-hex I merged the assoc-in pr, but I think this is unexpected:

(js/alert (pr-str (assoc-in {} [:foo :bar] 2)))
It fails because there's not already maps to update inside the structure

borkdude18:08:54

This also raises the question about what should happen for assoc-in! for unpopulated nestings, probably create a new map

borkdude18:08:28

Probably we should then also support nil to assoc (but what about assoc! ?)

Cora (she/her)19:08:17

a new map or a new object?

borkdude19:08:38

I'm working on a fix now. What I'm doing is it will depend on the top level object

borkdude19:08:59

That will lead to this behavior:

(is (eq #js {"0" #js {"1" 2}, "1" "foo"} (jsv! '(assoc-in {"1" "foo"} [0 1] 2))))
which is arguably weird, but not sure what else to do here :)

borkdude19:08:48

Perhaps if the key is a number, we should default to array

Cora (she/her)19:08:21

it's all so weird 😅

borkdude19:08:24

generating an empty array is weird since you can't assoc-in there with numbers ;)

Cora (she/her)19:08:53

> !0
true
> !1
false
> !2
false

Cora (she/her)19:08:54

the if (!baseType) is going to do something you don't intend because 0 is falsey in js

borkdude19:08:47

@corasaurus-hex good catch, I'll start from 1 then ;)

borkdude19:08:09

I also refactored assoc-in and assoc-in! to use the same code almost: https://github.com/clavascript/clavascript/commit/9e1fbbca8ff4443925b40f3c292f820e5b389d21 Hopefully I didn't miss anything

Cora (she/her)19:08:20

sorry I didn't hop onto this. I woke up really sick today

🍵 2
borkdude19:08:51

oh, no problem at all!

lilactown19:08:55

we're all just having fun I think Cora. I hope you feel better

1
Cora (she/her)19:08:22

I think the error message needs to be adjusted based on which function is calling it

borkdude19:08:54

true, I'll fix that

borkdude19:08:20

fixed

🎉 1
Cora (she/her)19:08:20

also you mentioned maybe not handling nil or underfined as the first argument to conj https://github.com/clavascript/clavascript/blob/9e1fbbca8ff4443925b40f3c292f820e5b389d21/core.js#L143-L145

Cora (she/her)19:08:28

not sure if you still feel that way or not

borkdude19:08:41

with these type number we can now write:

switch (typeConst(x)) {
  case MAP_TYPE: ...
  case ARRAY_TYPE ...
}

Cora (she/her)19:08:15

it would be nice to bench using a map of functions vs a switch/case

Cora (she/her)19:08:42

a real map, not an object, so you can use numbers as keys

Cora (she/her)19:08:48

or heck, an array

borkdude19:08:56

I'm afraid a map of functions will not make treeshaking work very well

Cora (she/her)19:08:22

the tension of bundle size vs performance. js is fun

borkdude19:08:17

OK, try (js/alert (pr-str (assoc-in {} [:foo :bar :baz] :quux))) in the playground: https://clavascript.github.io/clavascript/

borkdude19:08:12

and:

(def x {:a 1})

(assoc-in! x [:b :c] 2)

(js/alert (pr-str x))

borkdude19:08:28

beautiful ;)

Cora (she/her)19:08:28

are transducers going to be a thing in clavascript?

Cora (she/her)19:08:41

gosh, and sequences vs arrays

Cora (she/her)19:08:49

well, lists vs arrays

borkdude19:08:49

I think so, @lilactown just made an excellent writeup about iterators and transducers

Cora (she/her)20:08:29

wow, this is much trickier than I thought

lilactown20:08:10

this might not be the first time I've thought about seqs and transducers in JS... 😅

Cora (she/her)21:08:57

not that it really matters but my public acct is @corasaurus_hex -- the tweet tags me as @corasaurus_vex (my private acct)

borkdude21:08:32

oh sorry, this is what twitter came up with first :/

Cora (she/her)22:08:04

well that's weird but again totally fine

lilactown20:08:43

how do I add a new core var? I tried adding first and rest to resources/clava/core.edn but I'm still seeing

$ ./node_cli.js --show -e '(first [1 2 3])'
first([1, 2, 3]);

file:///Users/lilactown/Code/clavascript/.tmpSIEsgl/clava.mjs:1
first([1, 2, 3]);
^

ReferenceError: first is not defined
    at file:///Users/lilactown/Code/clavascript/.tmpSIEsgl/

borkdude20:08:45

@lilactown Resources is generated when you do bb dev

lilactown20:08:43

I've tried restarting too

borkdude20:08:45

And that .edn file is then pulled in via a macro. But macros are cached in CLJS, so maybe we need to "touch" the macro file as well

borkdude20:08:52

Try rm -rf .shadow-cljs

borkdude21:08:30

I guess we could also just generate .cljs file with that data

lilactown21:08:50

removing the .shadow-cljs dir fixed it

lilactown21:08:57

I'm thinking of opening PRs for some of these seq ops

lilactown21:08:06

should I create separate issues outside of my writeup?

lilactown21:08:41

I can also wait for people to chime in if more time in the hammock is wanted

borkdude21:08:45

if you want to get them merged in faster, I recommend smaller PRs, because it's easier to reason about small changes

borkdude21:08:54

I think the first/rest thing is a no-brainer

1
borkdude21:08:16

@lilactown Thanks for the PR. With eq you can also use a nested Clojure structure since it's thrown through clj->js in the eq function. You don't have to change this, just a FYI

borkdude22:08:31

Btw, one of the reasons I think clavascript is cool, is that you get a more direct way to write JS and with bun focussing on performance, writing FFI-ish stuff for that will be very interesting and fast

Cora (she/her)22:08:38

something I noticed in both ramda and transducers-js is that they check for whether Symbol is defined or not

Cora (she/her)22:08:51

I wonder which platforms this is still true for

borkdude22:08:59

well, we won't support that one, so we're cool :)

🎉 2
borkdude22:08:39

G'night enclava!

🌃 1