clojure-dev

borkdude 2023-02-13T11:27:02.275199Z

ClojureDart has a feature to destructure fields in keys destructuring using {:flds [a b c]} . In squint-cljs I've supported this as ^:js {:keys [a b c]} as to not have to make changes to clj-kondo. I guess Cursive would also have to make a change to support :flds - am I right @cfleming? I wonder if there's support for adopting this more broadly (in CLJS: @dnolen, in CLJ: @alexmiller). Field destructuring doesn't sound unique to ClojureDart and it would be nice not to have to make bespoke (albeit small) changes to static analyzers for Clojure dialects. cc @cgrand @baptiste-from-paris ❤️

souenzzo 2023-02-14T16:14:51.817869Z

still about :flds: Java 19 has a "record", that is a immutable class. https://docs.oracle.com/en/java/javase/19/language/records.html Its getters are not getField, but just field I think that :flds [foo] => .getFoo is not future-proof.

Alex Miller (Clojure team) 2023-02-14T16:16:13.281589Z

also many Java classes have .get methods with side effects - seems like that would be bad with destructuring

souenzzo 2023-02-14T16:17:04.505649Z

btw, bean an URL makes side-effects (and throws if you are offline)

borkdude 2023-02-14T16:18:42.138109Z

In Java :flds doesn't seem to be make much sense, maybe I should have started this conversation in #cljs-dev

Alex Miller (Clojure team) 2023-02-14T16:18:46.315189Z

field destructuring seems potentially interesting, but I'm not sure how much this would be needed in Java interop, probably more likely in cljs?

Alex Miller (Clojure team) 2023-02-14T16:18:53.239989Z

jinx

🙌 1
Alex Miller (Clojure team) 2023-02-14T16:19:31.940099Z

also, why not :fields ? I know the others are xyzs but they had useful first syllables/prefixes

baptiste-from-paris 2023-02-14T16:20:25.602279Z

@cgrand was really attached to a 4 letters word...

Alex Miller (Clojure team) 2023-02-14T16:20:53.008859Z

we use the more generic term "member" a lot too, did you consider :mems ?

baptiste-from-paris 2023-02-14T16:22:25.462239Z

no, I don't recall considering :mems

borkdude 2023-02-14T16:22:42.741769Z

or just

{:- [a b c]}
:P

cgrand 2023-02-14T16:23:11.443329Z

mems make me thinks of memories (or memo*) more than members

borkdude 2023-02-14T16:23:24.742429Z

:props

dnolen 2023-02-14T16:25:59.567219Z

in CLJS I find this pretty unattractive because of the advanced compilation problem

dnolen 2023-02-14T16:26:10.518409Z

it’s yet another thing to explain

dnolen 2023-02-14T16:26:18.870549Z

and there’s already a lot to explain

thheller 2023-02-14T16:26:58.265609Z

the alternative isn't much easier to explain imho

cgrand 2023-02-14T16:42:47.989939Z

Iirc the CLR has getters/setters too so @dmiller might be interested in supporting something like that

Alex Miller (Clojure team) 2023-02-14T17:03:38.825739Z

well as above, getters seem problematic (vs fields)

borkdude 2023-02-14T17:04:47.601099Z

JavaScript fields can also cause side effects (there are getters and setters, and you can implement your own trickery with Proxy)

borkdude 2023-02-14T17:06:07.988399Z

but if you wanted to call those fields/methods anyway, what's the problem in destructuring vs manually calling them?

borkdude 2023-02-14T17:08:24.979039Z

btw I'm not advocating for this feature, but just wanted to clear up what problem you see in this :)

💯 1
Alex Miller (Clojure team) 2023-02-14T17:15:18.675469Z

it may force some semantic ordering of actions that is not currently specified in merely accessing data

borkdude 2023-02-14T17:18:19.202159Z

gotcha

cgrand 2023-02-14T17:32:30.247059Z

Isn’t this tied to what’s idiomatic in a given ecosystem/community? In Java there are side-effecting getters in the “core” classes. Dart/Flutter seem more well behaved towards what getters do. In Clojure a deviant ILookup impl can break havoc. Well bean as mentioned above.

Alex Miller (Clojure team) 2023-02-14T17:46:17.060829Z

these are just things to think about

dmiller 2023-02-15T00:06:54.113249Z

@cgrand Very much interested in this problem from the viewpoint of ClojureCLR. It's been a problem.

cfleming 2023-02-13T11:43:27.202559Z

Yes, Cursive would have to make a change for this.

thheller 2023-02-13T11:55:19.483529Z

for CLJS it would make sense to have a :js/keys and :js/props to be able to easily access thing.x and thing["x"], since it makes a difference for the closure compiler which style you use

borkdude 2023-02-13T11:57:14.173109Z

the first gets renamed/optimized, the second not?

thheller 2023-02-13T11:59:29.013219Z

might get renamed yes, depends on externs. second is never modified

thheller 2023-02-13T12:00:39.225129Z

could use :js/flds to keep the 4 char theme 😉

borkdude 2023-02-13T12:01:20.889759Z

would be nice to have a unified way of doing this across CLJ dialect, which is kind of the point of the original post: not having to support dialect-specific stuff too much :)

borkdude 2023-02-13T12:01:33.171659Z

e.g. if the first is the most common, that could be the :flds way

cfleming 2023-02-13T12:02:33.985389Z

But any sort of analysis will need to know what the fields are coming from though, right? e.g. Java object, JS object, Dart object. Even if the surface syntax is the same.

cgrand 2023-02-13T12:03:07.094049Z

> Field destructuring doesn’t sound unique It’s more important in Dart because, fields have getter/setters. On the JVM it wouldn’t make sense as public fields are rare. Except if you are ready for :flds [foo] to expand to .getFoo.

borkdude 2023-02-13T12:03:26.842669Z

"host object" is the common theme here so I think the (static) analysis should (roughly?) be the same for all?

borkdude 2023-02-13T12:03:51.072289Z

well, clojure itself uses public fields, but I agree it's not that idiomatic ;)

borkdude 2023-02-13T12:04:03.184529Z

in JS it's way more common

cfleming 2023-02-13T12:04:48.101689Z

> Except if you are ready for :flds [foo] to expand to .getFoo. Having used Kotlin, I would actually like this, as well as (.foo x) (or perhaps (.-foo x))

borkdude 2023-02-13T12:05:27.066009Z

Note that (.foo x) will actually first try the method foo and then the field foo, if the method doesn't exist, in Clojure

borkdude 2023-02-13T12:05:40.450549Z

so :flds [getFoo] might work, if it expands to (.getFoo x)

cgrand 2023-02-13T12:05:45.897629Z

More generally what about static analyzer positing that {:unknown-kw [names ...]} in destrucring is going to bind the provided names.

cfleming 2023-02-13T12:05:57.220719Z

Yeah, but the .- might make it clearer that you’re expecting to access it like a property.

cgrand 2023-02-13T12:07:34.667769Z

> Having used Kotlin, I would actually like this, as well as (.foo x) (or perhaps (.-foo x)) But a class may have both a method named foo and one named .getFoo — unlikely but I’m sure there are specimens out there. How does kotlin deal with the ambiguity?

borkdude 2023-02-13T12:09:07.917439Z

> Yeah, but the .- might make it clearer that you’re expecting Didn't understand this. The expansion isn't visible to the user so it doesn't matter what you expand to, as long as it works for both fields and methods, which JVM Clojure already supports (if you don't automatically prepend get, just use the raw field/method name)

cgrand 2023-02-13T12:09:32.998889Z

While we are on sugarized form: we also have (.-prop! x v) as a shorthand for (set! (.-prop x) v) because 1/ setting fields is common beacuse of setters 2/ it works better with doto

🤔 1
cfleming 2023-02-13T12:11:06.626949Z

> How does kotlin deal with the ambiguity? I’m not sure, those classes are sufficiently rare that I’ve not seen one. But note that the method would be invoked as x.foo() whereas the property would be either a = x.foo or x.foo = b (desugaring to .getFoo() and .setFoo() respectively), so even for offending classes perhaps the syntax usually disambiguates - I’m not sure of all the details.

cgrand 2023-02-13T12:12:05.246359Z

without that what you can do on the JVM with (doto (Obj.) (.setFoo x) (.setBar y)) becomes (doto (Obj.) (-> .-foo (set! x)) (-> .-bar (set! y)). That’s why Cljd has (doto (Obj) (.-foo! x) (.-bar! y))

cfleming 2023-02-13T12:12:50.749739Z

Which is ugly - I had imagined it more for read access.

cgrand 2023-02-13T12:14:37.374359Z

@cfleming ah yes missing parens help disambiguating it, so it would be a dash, still room for conflict but more unlikely (public field vs setField)

cfleming 2023-02-13T12:15:47.238949Z

It’s not something I had thought about a lot, just the sort of thing that occurred to me in the shower.

cfleming 2023-02-13T12:17:36.234769Z

I like your (.-prop! x v) shorthand, I’ve never liked the set! syntax much.

borkdude 2023-02-13T12:21:04.771609Z

I think in CLJS this is valid:

(set! x -prop v)
I don't have a strong preference, but again I would have liked it if ClojureDart would have adopted that (maybe it does?) since tooling already supports this and -prop! looks like a regular field access, now you have to analyze the ending of the name for an exclamation mark, etc

cgrand 2023-02-13T12:26:21.616649Z

(set! x -prop v) 🤔 I though we had implemented something similar, but it was apprently ditched 😞 We can support it too. As for .xyz if you want to do anything useful with it you need to have dialect specific member resolution anyway.

borkdude 2023-02-13T12:27:23.623039Z

if you do some kind of type inference, yes, but clj-kondo currently does't choke on it. it doesn't even check that it's not valid to have the last "arguments" here (.-foo x 1 2 3) , but one day it might :)

borkdude 2023-02-13T12:27:41.148159Z

anyway, thanks for the heads up ...

cgrand 2023-02-13T12:27:48.984379Z

so the ! is not an issue then?

borkdude 2023-02-13T12:27:55.331909Z

currently not

borkdude 2023-02-13T12:28:08.912579Z

but it might be one day, I guess we'll find out :)

borkdude 2023-02-13T12:28:50.474919Z

not sure about Cursive here

cgrand 2023-02-13T12:29:57.893719Z

Well we have a couple of other stuff like dotless constructors, method names which are invalid symbols etc.

cgrand 2023-02-13T12:31:00.227149Z

Named arguments.

borkdude 2023-02-13T12:31:28.132499Z

@cgrand Is there a reference with these changes compared to other impls?

cfleming 2023-02-13T12:33:02.245969Z

I would also be very interested in that.

cgrand 2023-02-13T12:44:05.969749Z

First page of the cheatsheet gives a good summaryhttps://github.com/Tensegritics/ClojureDart/blob/main/doc/ClojureDart%20Cheatsheet.pdf

👍 1
cfleming 2023-02-13T12:45:24.550929Z

Great, thank you - I have been meaning to sit down and make a proper effort at properly supporting ClojureDart, this will help.

❤️ 4
cgrand 2023-02-16T08:55:11.780829Z

🤔 considering switching from :flds to .flds to clearly mark it as an interop feature

👎 1