announcements

cjohansen 2026-01-02T09:34:21.596729Z

https://github.com/cjohansen/m1p 2026.01.1 This https://clojars.org/no.cjohansen/m1p/versions/2026.01.1 adds a namespace for static analysis (by leaning on edamame), enabling e.g. tests that verify that all i18n keys in use are defined in a dictionary, and that they are used correctly. Check the https://github.com/cjohansen/m1p?tab=readme-ov-file#static-analysis. m1p is a small DIY i18n library for Clojure and ClojureScript. Both it's usage API and dictionary format are highly data-oriented, and it supports arbitrary values (e.g. not limited to string values). The Readme has https://github.com/cjohansen/m1p?tab=readme-ov-file, and there is https://replicant.fun/tutorials/i18n-alias/.

šŸŽ‰ 25
lread 2026-01-02T12:34:09.022189Z

The name alone is delightful!

cjohansen 2026-01-02T12:34:54.139099Z

@magnars and I were high-fiving a lot when we came up with that šŸ˜‚

šŸ˜‚ 4
šŸ™Œ 1
2
Alex Miller (Clojure team) 2026-01-02T16:02:31.282859Z

Released some more contribs with updated deps to Clojure 1.11.4: • spec.alpha 0.6.249 • tools.reader 1.6.0 • data.json 2.5.2 • java.data 1.4.120 • core.contracts 0.0.7 • math.combinatorics 0.3.2 • tools.namespace 1.5.1 • tools.deps 0.28.1569 • tools.deps.graph 1.2.98

šŸ‘ 8
šŸ‘šŸ» 1
šŸ‘šŸ½ 1
10
1
šŸŽ‰ 1
henrik 2026-01-02T23:04:28.852069Z

This is Quiescent plus supporting libraries. The goal of Quiescent is to make it a bit easier to work with and coordinate jobs running on virtual threads. https://github.com/multiplyco/machine-latch. A low-level synchronization primitive pretending to be a unidirectional state machine. https://github.com/multiplyco/scoped. Using ScopedValue from Clojure to chuck values across scopes (with fallback to ThreadLocal). https://github.com/multiplyco/pathling. A two-phased way of finding and updating values in Clojure data structures. https://github.com/multiplyco/quiescent. Composable async tasks focused on virtual thread embroidery.

šŸ‘€ 7
šŸ”„ 6
henrik 2026-01-04T16:15:36.318749Z

Yeah, spontaneously, it looks powerful. What makes me hesitate is that it’s likely going to result in a performance degradation. In contrast, I could likely use the latter to enhance performance. If given the collection predicate and key predicate separately, we can effectively cut branches off early. Let’s say we have some structure with a set of 1000 keywords and a vector of 1000 keywords. Let’s say we want to match all keywords in a vector when on even indices. Variant one: Predicate: (and (vector? data) (even? k) (keyword? v)) • ~2k vector? checks • 1k even? checks • 500 keyword? checks = ~3.5k predicate checks Variant two: {:coll-ped vector? :key-pred even? :val-pred keyword?} or something. • 2 vector? checks • 1k even? checks • 500 keyword? checks = ~1.5k predicate checks Everything in the algorithm has been otherwise trimmed to the point where running the predicate accounts for a significant portion of the total runtime. But something seems off with filtering on key and matching on key with :include-keys, I’m not sure about how to think of the interaction between them in either case. Makes me think I’ve complected something that shouldn’t be.

henrik 2026-01-03T10:21:31.650349Z

No, currently it only receives the value. A workaround would be to terminate the scan one step earlier: at the eg. vector level rather than the value level, and then perform the update there. There’s no way of expressing eg. ā€œmatch keywords on even indicesā€ right now. It might be the type of thing I would reach for Specter to express.

dominicm 2026-01-03T10:46:09.263029Z

Yeah, specter comes to mind but I find it really difficult to remember how to use it when I reach for it every 12 months or so.

henrik 2026-01-03T11:47:28.028309Z

Technically the information could be made available, at least for transformation ops, where the key is kept anyway in order to construct the path to the value. But the predicate would get a rather odd signature: [data k v], but maybe that’s fine. Binding the data structure itself to data would be necessary, I think, in order to distinguish a case like k == 0, v == 0 if you’re in a set or a vector (since with sets, the key is logically the same as the value).

dominicm 2026-01-03T11:49:35.908259Z

I figured you'd have it for that case, yeah. I'd even be happy with being able to look it up in the navigator separately.

henrik 2026-01-03T11:53:03.243039Z

Right, but I think the correct way to do it would be to disambiguate during scanning rather than during update. If you can exclude the false positives during scanning, the resulting navigation structure becomes smaller (fewer allocations), and during update, it doesn’t have to lift the structure just in order to do a no-op update.

šŸ‘ 1
henrik 2026-01-03T12:54:24.831819Z

How about you receive [data k v] , and you respond if the key was a match, the value was a match, or both were matches?

henrik 2026-01-03T12:58:11.801059Z

The other way would be to add more predicates to the options map: one predicate that filters the container / coll, and another that filters the key.

henrik 2026-01-03T13:00:07.031839Z

So in one world, you do something like

(path-when data
  (fn [data k v]
    (when (and (vector? data) (even? k) (keyword? v))
      VALUE-MATCH)))
Using sentinel objects for you to tell it if the key matched, the value matched, or both matched. Could be more powerful than simple predicates. Removes the need for an :include-keys options switch. Odd API perhaps.

henrik 2026-01-03T13:01:09.242349Z

In the other world:

(path-when data keyword?
  {:coll-pred vector?
   :key-pred  even?})

dominicm 2026-01-03T16:59:53.808409Z

I think I prefer the first, it reminds me more of reduce-kv and I think it would be more flexible for considering both keys and values in your matching.

dominicm 2026-01-03T07:22:33.442719Z

Pathling is a really neat library. I love seeing things like this for open data modification. I think there's a gap for something like this to step in and be a really simple way to do a lot of the kinds of things people need. In terms of the navigation structure, does the API include any information from there? Eg if I wanted to apply inc only to even-indexed items? Or under keys starting with "a"

šŸ™ 1
neumann 2026-01-05T15:51:28.449759Z

@henrik By the way, there is a preexisting Clojure library called ā€œquiescentā€. It was one of the early ReactJS wrappers back in the day. It hasn't been updated in years, but it will come up first if you search ā€œClojure quiescentā€: https://github.com/levand/quiescent

šŸ™ 1
henrik 2026-01-05T15:59:40.614829Z

Ah, I should have checked. Wow, a decade ago now, and it looks like it was quite popular. Well, c’est la vie.

henrik 2026-01-05T16:00:47.930279Z

I can do the other thing though: put a notice for people looking for the React wrapper, should this ever be reranked on top.

neumann 2026-01-05T16:02:39.315999Z

It's also old enough to not be namespaced in Clojars: https://clojars.org/quiescent

neumann 2026-01-05T16:03:23.982039Z

Quiescent definitely had its day in the sun. I think it was the first React wrapper I used in anger.

šŸ˜„ 1
henrik 2026-01-05T16:03:53.769759Z

Well, it shows you how harmful namespacing is. Without namespaces, I would have noticed when I tried to upload it.

henrik 2026-01-05T16:05:35.278149Z

I’m joking of course.

šŸ˜‚ 1
neumann 2026-01-05T16:07:22.438989Z

Ah yes. Convenient for access control policies, but not great for maintaining uniqueness.

henrik 2026-01-05T16:08:53.117669Z

Right. I really should have checked, but in this case I think the statute of limitations has probably elapsed for Quiescent (senior).

šŸ‘ 1
dominicm 2026-01-05T16:09:34.277219Z

I think this is the beauty of namespaces, nobody can squat the good name

henrik 2026-01-05T16:10:56.575009Z

Agreed, namespaces are great in many contexts. Sometimes even the ones where you think you have complete control.

neumann 2026-01-05T16:11:32.697599Z

If you can win over the SEO, more power to you! I only mentioned it because I figured you'd rather know than be surprised further down the road.

šŸ™ 1
henrik 2026-01-05T16:14:13.429169Z

Thank you! I have no need as such to promote it further than it will travel by itself (though of course it’s satisfying if I can help someone through it).

henrik 2026-01-05T16:14:48.950829Z

Also, I wonder how many people will just search for Quiescent and Clojure and go to the first result returned. ā€œOh well, guess I’m building a React app.ā€

neumann 2026-01-05T16:20:05.297299Z

It makes it hard to spread by name. It reminds me of the poorly named ā€œschemaā€ which came to be referred to as ā€œplumatic schemaā€. To share it by name, you end up adding another word. Eg ā€œquiescent vthreadsā€ or something like that—at least until you top the SEO charts.

henrik 2026-01-05T16:25:03.802599Z

That’s true, although maybe mitigated by the alternative being a decade old. The truth is that the name and concept, and the behaviour I associate with it, is so settled into my mind now that I’d find it jarring to change. This has lived for a long time in a production codebase before now. The word ā€œquiescentā€ came to be because I tried my best not to use ā€œcompletedā€, ā€œendedā€ and other words with informal connotations, since the internal lifecycle effectively has several ends, depending on how you define ā€œcompleteā€.

neumann 2026-01-05T16:30:22.696129Z

I do love the name! Just to be clear, I'm not saying anything about needing to change the name or not.

ā¤ļø 1
henrik 2026-01-05T16:31:06.887169Z

Ah, right! Sorry, I guess I was projecting that.