squint

borkdude 2024-06-03T15:56:20.952439Z

Idea: support the optional chaining operator though some syntax

> x = {a: {b: {c: 1}}}
{ a: { b: { c: 1 } } }
> x?.a?.b?.c
1 
E.g.:
(def x {:a {:b {:c 1}}}) 
(.. x ?-a ?-b ?-c)
Right not this conflicts if there is actually a ?-a etc function. I've also considered supporting:
(-> x?.a?.b ...)
directly, but this also conflicts if there's already a x? variable somewhere. It would be nice to have a unambigious syntax for this, but perhaps it's not worth it. E.g. (..? x -a -b -c)

littleli 2024-06-04T08:01:25.961539Z

Don't want to stress out, take it as a FYI. There are also other peculiar operators. Like null ?? 'default' called nullish coalescing operator. And there is nullish coalescing assignment operator ??=

const a = { duration: 50 };

a.duration ??= 10;
console.log(a.duration);
// Expected output: 50

a.speed ??= 25;
console.log(a.speed);
// Expected output: 25
๐Ÿ™ˆ

borkdude 2024-06-04T08:23:50.015739Z

The nullish one has support on the main branch

littleli 2024-06-04T08:46:14.086499Z

catjam Awesome

borkdude 2024-06-03T15:56:34.061369Z

๐Ÿงต

2024-06-03T16:04:36.880559Z

how's this different than get-in early returning on nil?

borkdude 2024-06-03T16:05:10.596769Z

it's compiled closer to what a JS engine supports, thus likely far better performance

2024-06-03T16:06:24.487559Z

maybe -?> or get-in-? or get?-in lol

borkdude 2024-06-03T16:06:45.452649Z

get-in is dynamic, so that doesn't work, the point here is that you know the properties at compile time

2024-06-03T16:07:34.524539Z

i'm suggesting a new function/macro that creates the necessary syntax

borkdude 2024-06-03T16:08:24.184549Z

.. seems far closer than -> though, since the arguments to -> are still dynamic

๐Ÿ‘ 1
2024-06-03T16:09:02.391519Z

maybe ..? which joins with ?. instead of .?

borkdude 2024-06-03T16:10:28.291399Z

That is what I already suggested ;)

2024-06-03T16:10:43.729869Z

oops ๐Ÿ˜…

borkdude 2024-06-03T16:12:16.667429Z

Here's a result from the squint REPL:

user=> (simple-benchmark [x {:a {:b {:c 1}}} f (js/eval "(x) => x?.a?.b?.c")] (f x) 100000000)
[x {:a {:b {:c 1}}} f (js/eval "(x) => x?.a?.b?.c")], (f x), 100000000 runs, 133 msecs
undefined
user=> (simple-benchmark [x {:a {:b {:c 1}}} f #(some-> % :a :b :c)] (f x) 100000000)
[x {:a {:b {:c 1}}} f (fn* [%1] (some-> %1 :a :b :c))], (f x), 100000000 runs, 1813 msecs

littleli 2024-06-03T16:54:43.529289Z

and what about allow for this case javascript syntax directly, example:

(def x {:x {:z {}})
(js/alert #js-expr x?.y?.z) -- undefined
how crazy is that?

littleli 2024-06-03T16:58:19.451379Z

Otherwise, this looks quite good: (..? x -a -b -c) as suggested above, but may lead to compatibility issue with cljs. Don't know how far you want to have parity.

littleli 2024-06-03T17:01:23.963429Z

btw how do you deal with the fact that properties in js can have spaces in them? In fact how the hell is cljs dealing with that? ๐Ÿ˜„

obj?.["evil trickster"]

๐Ÿ‘ป 1
borkdude 2024-06-03T17:25:25.213079Z

I already explained the problem with the "directly" approach above

borkdude 2024-06-03T17:25:49.024739Z

compatibility: squint already supports more than CLJS in some areas, so that's not an issue to me

borkdude 2024-06-03T17:26:16.198749Z

spaces in properties: CLJS is dealing with that in the same way that JS is, you write a string

borkdude 2024-06-03T17:26:29.485319Z

tl;dr: so far the ..? option looks best

littleli 2024-06-03T17:29:39.270759Z

Right. Also directly is kinda unsafe when it comes to code it can produce. So yeah. ..? looks the best

borkdude 2024-06-03T17:30:43.172409Z

properties are munged as well, another source of breakage:

cljs.user=> (str (fn [x] x.dude?.quux))
"function (x){\nreturn x.dude_QMARK_.quux;\n}"

borkdude 2024-06-03T17:31:00.024519Z

when you create JS objects like #js {:dude? 1}

littleli 2024-06-03T17:31:09.614489Z

yeah I noticed that at playground

borkdude 2024-06-03T17:31:32.449099Z

cool, thanks for thinking along

2024-06-03T18:41:07.418299Z

I think ClojureDart has a kind of a.b.c syntax. But personally I think ..? or ?.. makes most sense in not being too jarring of a syntax compared to the rest. But -?prop would also be nice. And technically if that exists, people can build their own ..? or -?> macros no? So I like how that mimics Clojure's interop more. In that the real interop syntax is -prop and (. obj -prop)

john 2024-06-03T18:50:02.016139Z

What does the question mark mean here?

john 2024-06-03T18:52:34.709149Z

Aaaah, I was going to say, that looks super ugly IMO, but if it's already a thing in js yeah that might be the simplest all around, especially for js folks

john 2024-06-03T18:54:47.685649Z

I guess keys that have question marks at the end will just have two question marks? Any risk of clobbering?

borkdude 2024-06-03T18:55:33.837119Z

Didibus his suggestion suffers from the same conflict issue, namely props kan start with question marks

john 2024-06-03T18:55:34.303479Z

Oh sorry, I misread

john 2024-06-03T18:56:32.046269Z

Your initial syntax doesn't have that problem though, right?

john 2024-06-03T19:01:12.957469Z

Yeah I think ..? Looks best.

john 2024-06-03T19:04:09.851509Z

I personally like the perc https://github.com/johnmn3/perc. More clojurey

john 2024-06-03T19:04:49.697899Z

But you can't do double colons in clj's reader, so it's not a perfect translation

john 2024-06-03T19:06:13.307679Z

So you'd do something like #.? :x:y

john 2024-06-03T19:06:23.758639Z

As a reader macro

john 2024-06-03T19:07:13.337389Z

I guess you wouldn't need double colons since js keys aren't ns qualified

borkdude 2024-06-03T19:12:27.381679Z

too perly for my taste (even though perl may not have this, I think it looks too weird)

john 2024-06-03T19:16:44.893949Z

What about with spaces? Like a get-in or thread navigation, just starting with ..?

john 2024-06-03T19:17:08.787139Z

So no special syntax for the keys

john 2024-06-03T19:18:18.379779Z

Eh, maybe the dash is more understood for that interop use case

๐Ÿ‘ 1
2024-06-03T21:45:00.016839Z

What about ?-prop , that would actually be closer to the JS ?. operator. So ya it could be ?-?prop but that's true in JS as well a?.?prop no ? Or, instead of touching the prop, like there is . in Clojure for interop, there could also be ?. special operator. And again you could then implement whatever macro on top.

2024-06-03T21:55:29.698879Z

The downside is it's harder to combine. If you want to do: a.b?.c.d With the prop syntax you could do:

(.. a -b ?-c -d)
With a different operator you need to do:
(. (?. (. a -b) -c) -d)

borkdude 2024-06-03T21:56:54.524989Z

Again, ?-prop isn't shielded from conflicts.

(.. x ?-prop) ;; => (.?-prop x)

2024-06-03T21:58:12.358109Z

You mean from existing code ? Wouldn't current code have has to be written as -?-prop

2024-06-03T21:58:47.785519Z

I'm thinking Clojurescript, because in Clojurescript you have to use dash for prop access no? Unlike in Clojure

borkdude 2024-06-03T21:58:50.749159Z

let me write an example for you

borkdude 2024-06-03T22:00:57.894029Z

every prop that doesn't start with a dash is handled as a function call in the .. macro

2024-06-03T22:05:36.234049Z

Right I see. I think I assumed that if squint added ?- syntax it would also special handle it where it needs too. Is it the .. macro that does that? I thought it was . itself that did that

borkdude 2024-06-03T22:07:46.601339Z

you're right, it's .

borkdude 2024-06-03T22:08:35.466319Z

squint could add special handling in .. for ? but this would break existing behavior

borkdude 2024-06-03T22:09:01.431299Z

this is why I thought about making a ..? macro which is similar

2024-06-03T22:15:28.143219Z

So the issue with ?-prop is that it conflicts with functions that start with ?- if I understand correct? And so you can't tell if that's a function or a prop anymore?

john 2024-06-03T22:16:22.848479Z

And I feel like I'm looking at an anaphoric datalog query

2024-06-03T22:17:14.038779Z

And your ..? would be a special form and directly compile to JS?

borkdude 2024-06-03T22:17:58.220559Z

..? would be a simlar macro as .. but just with the optional chaining

borkdude 2024-06-03T22:18:59.680859Z

or maybe there should be a .? special form then as well (`.` is also a special form, not a macro)

2024-06-03T22:19:50.517759Z

I was thinking there needs to be a special form no? Otherwise how do you know to compile with ?. instead of . ?

john 2024-06-03T22:20:50.795909Z

You could also make it a special thread macro, so you can intersperse different logic between the fns in the chain. And then you can use it like a chain if you want.

john 2024-06-03T22:22:13.215929Z

But I guess we're going for a thing that compiles down optimally to an idiomatic js optional chain

borkdude 2024-06-03T22:22:46.540239Z

@didibus you can hack it together without a special form using js* I believe, but it isn't pretty

2024-06-03T22:27:22.707949Z

Ah I see. Well I guess how it's implemented doesn't matter that much. I think ..? would probably be convenient. But like John was saying, I feel ideally (and maybe that's not doable), something that lets me mix and match between accessing Clojure values and JS props and lets me choose which access I'm the chain I want to be null-safe and which I want an error from would be the nicest to have.

borkdude 2024-06-03T22:28:11.591929Z

Clojure values and JS props? in squint everything is JS

๐Ÿค” 1
2024-06-03T22:28:35.107459Z

So I was hoping you could do: (-> foo :bar (some-fn) ?-prop -prop) as a convoluted example ๐Ÿ˜…

borkdude 2024-06-03T22:29:01.542889Z

calling it a night, thanks folks!

๐Ÿ‘ 1