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)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
๐The nullish one has support on the main branch
catjam Awesome
๐งต
how's this different than get-in early returning on nil?
it's compiled closer to what a JS engine supports, thus likely far better performance
maybe -?> or get-in-? or get?-in lol
get-in is dynamic, so that doesn't work, the point here is that you know the properties at compile time
i'm suggesting a new function/macro that creates the necessary syntax
.. seems far closer than -> though, since the arguments to -> are still dynamic
maybe ..? which joins with ?. instead of .?
That is what I already suggested ;)
oops ๐
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 msecsand 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?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.
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"]
I already explained the problem with the "directly" approach above
compatibility: squint already supports more than CLJS in some areas, so that's not an issue to me
spaces in properties: CLJS is dealing with that in the same way that JS is, you write a string
tl;dr: so far the ..? option looks best
Right. Also directly is kinda unsafe when it comes to code it can produce. So yeah. ..? looks the best
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}"when you create JS objects like #js {:dude? 1}
yeah I noticed that at playground
cool, thanks for thinking along
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)
What does the question mark mean here?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
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
I guess keys that have question marks at the end will just have two question marks? Any risk of clobbering?
Didibus his suggestion suffers from the same conflict issue, namely props kan start with question marks
Oh sorry, I misread
Your initial syntax doesn't have that problem though, right?
Yeah I think ..? Looks best.
I personally like the perc https://github.com/johnmn3/perc. More clojurey
But you can't do double colons in clj's reader, so it's not a perfect translation
So you'd do something like #.? :x:y
As a reader macro
I guess you wouldn't need double colons since js keys aren't ns qualified
too perly for my taste (even though perl may not have this, I think it looks too weird)
What about with spaces? Like a get-in or thread navigation, just starting with ..?
So no special syntax for the keys
Eh, maybe the dash is more understood for that interop use case
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.
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)Again, ?-prop isn't shielded from conflicts.
(.. x ?-prop) ;; => (.?-prop x)You mean from existing code ?
Wouldn't current code have has to be written as -?-prop
I'm thinking Clojurescript, because in Clojurescript you have to use dash for prop access no? Unlike in Clojure
let me write an example for you
every prop that doesn't start with a dash is handled as a function call in the .. macro
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
you're right, it's .
squint could add special handling in .. for ? but this would break existing behavior
this is why I thought about making a ..? macro which is similar
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?
And I feel like I'm looking at an anaphoric datalog query
And your ..? would be a special form and directly compile to JS?
..? would be a simlar macro as .. but just with the optional chaining
or maybe there should be a .? special form then as well (`.` is also a special form, not a macro)
I was thinking there needs to be a special form no? Otherwise how do you know to compile with ?. instead of . ?
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.
But I guess we're going for a thing that compiles down optimally to an idiomatic js optional chain
@didibus you can hack it together without a special form using js* I believe, but it isn't pretty
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.
Clojure values and JS props? in squint everything is JS
So I was hoping you could do:
(-> foo :bar (some-fn) ?-prop -prop) as a convoluted example ๐
calling it a night, thanks folks!