clj-commons

2021-12-20T18:09:30.101600Z

I opened a small PR for ordered and @borkdude was kind enough to remind me to write a good PR message, but his comment gave me pause. Is there a defined process for writing PRs for the clj-commons libraries? I didn't see anything in meta. I recently reread Rich Hickey’s stance on patches instead of PRs and optimizing for the maintainer’s time instead of the contributor’s, so I want to make sure that as I write PRs for clj-commons libraries, I’m respecting existing conventions and the maintainers.

borkdude 2021-12-20T18:53:06.102Z

I think starting with a patch is the incorrect order.

borkdude 2021-12-20T18:53:23.102200Z

This is what I recommend for my own projects: https://github.com/clj-kondo/clj-kondo/blob/master/doc/dev.md#workflow

👍 2
2021-12-20T18:59:37.102800Z

That’s a good workflow, thank you. I’ll keep it in mind going forward

borkdude 2021-12-20T19:17:10.103400Z

I think those words really resonated with me

2021-12-20T19:42:08.103800Z

yeah, that makes a lot of sense

slipset 2021-12-20T20:44:23.106Z

Hey @nbtheduke, thanks for taking us into consideration, much appreciated. I’ve been meaning to write a blog post on the subject of how to get patches submitted (in general), but haven’t quite gotten around to it. I think @borkdudes recommendation is very valid, but I think he left out one somewhat important alternative, the alternative of doing nothing 🙂

borkdude 2021-12-20T20:45:18.106500Z

that falls under "if we reach an agreement", if...

borkdude 2021-12-20T20:45:47.107100Z

and enumerating alternatives is also in there

2021-12-20T20:46:36.109Z

Right. I look at the code and see a comment from the original maintainer that says “This is broken” and I think, “I can fix it!” so I write the fix, but it’s probably better to start with opening an issue and saying “Is this still broken? Is the comment outdated?” etc

slipset 2021-12-20T20:52:32.113200Z

Which comment is this?

2021-12-20T20:55:23.113700Z

that comment also discusses a related issue to the PR that I opened: “what should the not-found value be?“, but in all of my experimentations I couldn’t actually produce a bug for the transient ordered map, only the persistent ordered map, so I didn’t make any changes for the transient ordered map

slipset 2021-12-20T20:57:12.113900Z

It’s an interesting study, since it’s obviously a corner case. Not asking you to do this, but it would be interesting to see if you could write a pathalotical example that proved the comment right. That could be done in a gist and linked to in an issue description. The corner case is then a transient map which contains itself as a value. We could discuss the likelyhood of that happen and then contrasted that with the complexity of potential solutions. One solution could be to add a comment somewhere which said that contains was b0rken for transient maps which contained themselves as values.

2021-12-20T20:58:01.114100Z

from a “code cleanliness”/“code does the thing I intend” standpoint, I find it awkward that this is cast to a MapEntry if the given key doesn’t exist, but for whatever reason, it works. If so desired, creating a (def ^:private transient-not-found (MapEntry. (gensym) (gensym))) is easily doable but that’s well outside my original PR

2021-12-20T20:59:05.114300Z

oh, TransientOrderedMap doesn’t implement ITransientAssociative2 at all, so contains? and find fundamentally don’t work on it regardless of it containing itself or not

2021-12-20T21:04:08.115500Z

flatland.ordered.map=> (-> (transient {}) (assoc! :a 1) (contains? :a))
true
flatland.ordered.map=> (-> (transient (ordered-map)) (assoc! :a 1) (contains? :a))
Execution error (IllegalArgumentException) at flatland.ordered.map/eval8248 (form-init14525214835311855450.clj:1).
contains? not supported on type: flatland.ordered.map.TransientOrderedMap

slipset 2021-12-20T21:05:01.115700Z

I’d argue that’s worse 🙂

2021-12-20T21:05:46.115900Z

using a private MapEntry as the “not found” value instead of coercing the transient itself as a MapEntry?

slipset 2021-12-20T21:06:33.116100Z

No, that contains? throws because it’s not implementing the correct protocol.

2021-12-20T21:07:04.116300Z

oh, yeah, it’s not good at all haha

2021-12-20T21:08:28.116500Z

I documented the issue here: https://github.com/clj-commons/ordered/issues/69 and have a branch with the described fix but didn’t want to jump the gun given the rest of the conversation about “going slowly and discussing solutions first” lol

Alex Miller (Clojure team) 2021-12-20T21:23:56.117Z

Note that ITransientAssociative2 was added in I think 1.9, so implementing that requires Clojure 1.9+

Alex Miller (Clojure team) 2021-12-20T21:24:24.117200Z

https://clojure.atlassian.net/browse/CLJ-700

2021-12-20T21:25:16.117400Z

excellent historical information, thank you so much

slipset 2021-12-20T20:46:43.109300Z

Yeah, but I find myself often forgetting that doing nothing is a valid alternative, with some nice tradeoffs, and besides, if doing nothing is an alternative, you’ll reach an agreement 🙂

👍 2
borkdude 2021-12-20T20:47:09.109700Z

I love doing nothing

👍 1
2022-01-13T14:20:11.000700Z

Really? 🤣

borkdude 2022-01-13T14:20:50.001100Z

Hey, it looks like I took 2nd of January off, didn't I.

🤣 1
2022-01-13T14:21:06.001300Z

slacker 😉

slipset 2021-12-20T20:47:24.110100Z

That’s why I ended up as a programmer 🙂

slipset 2021-12-20T20:50:41.112700Z

Back to the question at hand though. I find it fairly hard and extremely annoying to have to enumerate different solutions to a problem which I already have thought out the best solution. For me. But other people might have other use cases and priorities. This is why we need the discussions even though they’re both hard and annoying. Because for some people, it might be more important that the set fns are fast than that they behave correctly on garbage input.

2021-12-20T21:00:01.115400Z

Yeah, I too find it really hard, hah, but it’s a good practice and provides external documentation for discovering the “best” solution vs “the one that most readily came to my fingertips”