Fork me on GitHub
#clojure-dev
<
2023-02-13
>
borkdude11:02:02

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 ❤️

cfleming11:02:27

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

thheller11:02:19

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

borkdude11:02:14

the first gets renamed/optimized, the second not?

thheller11:02:29

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

thheller12:02:39

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

borkdude12:02:20

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 :)

borkdude12:02:33

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

cfleming12:02:33

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.

cgrand12:02:07

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

borkdude12:02:26

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

borkdude12:02:51

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

borkdude12:02:03

in JS it's way more common

cfleming12:02:48

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

borkdude12:02:27

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

borkdude12:02:40

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

cgrand12:02:45

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

cfleming12:02:57

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

cgrand12:02:34

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

borkdude12:02:07

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

cgrand12:02:32

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

2
cfleming12:02:06

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

cgrand12:02:05

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

cfleming12:02:50

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

cgrand12:02:37

@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)

cfleming12:02:47

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

cfleming12:02:36

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

borkdude12:02:04

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

cgrand12:02:21

(set! x -prop v) :thinking_face: 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.

borkdude12:02:23

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 :)

borkdude12:02:41

anyway, thanks for the heads up ...

cgrand12:02:48

so the ! is not an issue then?

borkdude12:02:55

currently not

borkdude12:02:08

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

borkdude12:02:50

not sure about Cursive here

cgrand12:02:57

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

cgrand12:02:00

Named arguments.

borkdude12:02:28

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

cfleming12:02:02

I would also be very interested in that.

cfleming12:02:24

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

❤️ 6
souenzzo16:02:51

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)16:02:13

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

souenzzo16:02:04

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

borkdude16:02:42

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

Alex Miller (Clojure team)16:02:46

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)16:02:31

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

baptiste-from-paris16:02:25

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

Alex Miller (Clojure team)16:02:53

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

baptiste-from-paris16:02:25

no, I don't recall considering :mems

borkdude16:02:42

or just

{:- [a b c]}
:P

cgrand16:02:11

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

dnolen16:02:59

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

dnolen16:02:10

it’s yet another thing to explain

dnolen16:02:18

and there’s already a lot to explain

thheller16:02:58

the alternative isn't much easier to explain imho

cgrand16:02:47

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

Alex Miller (Clojure team)17:02:38

well as above, getters seem problematic (vs fields)

borkdude17:02:47

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

borkdude17:02:07

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

borkdude17:02:24

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

💯 2
Alex Miller (Clojure team)17:02:18

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

cgrand17:02:30

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)17:02:17

these are just things to think about

dmiller00:02:54

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

cgrand08:02:11

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

👎 2