announcements

pez 2026-04-08T07:31:07.652079Z

#epupp epupp is a browser extension that can start a Clojure REPL (using #scittle) in any browser page you visit: letting you inspect, collect, and modify the page as you wish, from the convenience of your editor and/or AI harness. Epupp also supports userscripts, Tamper Monkey-ish. Available from the https://chromewebstore.google.com/detail/bfcbpnmgefiblppimmoncoflmcejdbei and https://addons.mozilla.org/firefox/addon/epupp/. (Safari support not on par yet, but you can still use it.) I am dying for your questions and feedback! ❀️ πŸ™ https://github.com/PEZ/epupp/releases/tag/v0.0.15 just out: β€’ Add support for Epupp dependencies (Epupp .cljs code as libraries) β—¦ Building on the existing :epupp/inject manifest functionality β—¦ Closes: https://github.com/PEZ/epupp/issues/10 β—¦ Local libraries injected via scheme β—¦ Transitive dependencies supported β—¦ External dependencies: inject code from GitHub raw content hosts, including gists, via HTTPS URLs β–ͺ︎ Supported hosts: , β–ͺ︎ SHA-pinned for immutable, reproducible dependency resolution β–ͺ︎ Fetched and cached on script save, injected from cache at page load β€’ Fix bug with document-start scripts being run twice

1
9
❗ 1
πŸ‘€ 2
πŸŽ‰ 6
πŸ¦„ 1
pez 2026-04-08T07:32:20.581149Z

I think this should solve your needs to share code between userscripts, @neumann. Please let me know how you fare!

practicalli-johnny 2026-04-08T12:56:12.610219Z

https://github.com/practicalli/nvim-astro - a rich configuration to support Clojure development with Neovim 0.12 Clojure REPL Driven Development via Olical/Conjure project, with structural editing support from nvim-paredit & nvim-parinfer (par-par plugin) Treesitter support for syntax highlighting and incremental language parsing (faster plugin magic) Clojure LSP diagnostics automatically installed via Mason Packages lazy loaded to keep resource use to a minimum Git client support via LazyGit and Neogit (like Magit for Emacs or Edamagit for VSCode) Release changes: - astronvim: switch to released v6 - practicalli: remove neo-tree, replace with snacks.picker.explorer - community: add markdown pack including marksman tool - community: add bash pack to support zsh and bash scripts - readme: update repo name and overview https://github.com/practicalli/nvim-astro/releases/tag/2026-04-08

7
4
πŸŽ‰ 4
yogthos 2026-04-08T14:54:55.892969Z

I ended up porting tree-sitter-clojure to wasm for a project I'm working on, might be generally useful since it doesn't have native dependencies https://www.npmjs.com/package/@yogthos/tree-sitter-clojure

❗ 5
πŸŽ‰ 10
2026-04-08T15:59:47.141059Z

can i ask what the project is? im intrigued

yogthos 2026-04-08T16:11:57.723229Z

oh, I went down a rabbit hole reading this paper https://arxiv.org/abs/2305.00813 and ended up making mcp which allows a LLM to load source code into a js sandbox, parse it with tree sitter, and then ask questions about stuff like function usage, call graphs, etc., which they normally have a hard time doing since it just ends up being ad hoc grepping https://github.com/yogthos/chiasmus

2026-04-08T16:14:20.880029Z

so you're using tree sitter parse trees as like a langauge-agnostic intermediate representation for z3 to analyze things? and it works?

yogthos 2026-04-08T16:16:05.853119Z

yup seems to be working, still playing with it to tune things, but promising so far

πŸ‘€ 1
yogthos 2026-04-08T16:16:56.759089Z

prolog actually appears to be the more useful thing, cause you can trivially ask graph based questions using it

πŸ€” 1
2026-04-08T16:29:03.854509Z

what sort of questions can you ask and what answers do you get back

yogthos 2026-04-08T16:31:23.795899Z

I have some examples here https://github.com/yogthos/chiasmus?tab=readme-ov-file#why-chiasmus_graph-over-grep

yogthos 2026-04-08T16:32:21.264119Z

the really interesting application is verification because you can ask questions like if there's only a specific path towards a particular bit of code

2026-04-08T16:36:09.739359Z

even just asking for callchain would be super useful. especially if it had a skill/tool description with some example questions that work well. i find that grepping around to see callchains happens pretty often.

yogthos 2026-04-08T16:37:04.728229Z

yeah the trick is using templates, the llm can search for a template that roughly matches what it's trying to do, and fill in the blanks

2026-04-08T16:37:39.322609Z

fuzzy templates? is that a tree sitter thing or a prolog thing

yogthos 2026-04-08T16:37:57.727989Z

templates are for the solver and prolog

yogthos 2026-04-08T16:38:16.386679Z

tree sitter just gives the initial graph of the code that gets adapted

yogthos 2026-04-08T16:38:38.108109Z

I'm really surprised this sort of stuff isn't standard in tools like claude code, it would make so much more sense to load the project into a graph, and then work against that instead of grepping files constantly

⚑ 2
2026-04-08T16:38:44.757019Z

https://tree-sitter.github.io/tree-sitter/using-parsers/queries/1-syntax.html i was thinking of this.

yogthos 2026-04-08T16:38:48.808319Z

like the way these tools work is the dumbest way possible

πŸ‘ 1
2026-04-08T16:39:12.913259Z

but prolog templates seem much better.

2026-04-08T16:39:38.185629Z

the language agnostic graph/tree sitter seem slkie the missing piece for these tools.

yogthos 2026-04-08T16:40:00.328669Z

yeah that would be the ideal

2026-04-08T16:40:37.100949Z

can it apply edits using the graph? like somehow mutate the graph then go back to source? that would probably be super tricky and whitespace would be messed up

2026-04-08T16:40:54.929359Z

or maybe text edits + trees sitter feedback would be enough

yogthos 2026-04-08T16:41:33.239019Z

yeah you'd need to do a bit more work there, but in theory you could parse the project into graph representation, then work against it, and serialize back when you're done

yogthos 2026-04-08T16:42:01.316059Z

I'm actually playing with this approach here https://github.com/mycelium-clj/sporulator

yogthos 2026-04-08T16:42:07.585659Z

but in a more restricted context

2026-04-08T16:42:30.540119Z

syntax/paren errors in trees-titter graph might be more useful than the clj explosions.

πŸ’― 1
yogthos 2026-04-08T16:42:30.763639Z

with Clojure we can just use the repl as the workspace

2026-04-08T16:43:56.427469Z

i looked at sporulator and i was a bit confused. visual buidler? are there screenshots?

yogthos 2026-04-08T16:47:45.497959Z

there are two parts to it, there is a UI component, and a server side API driven part which is doing all the heavy lifting

yogthos 2026-04-08T16:48:06.262029Z

the idea there is to box the LLM into doing very specific tasks, and doing a lot of the verification mechanically

yogthos 2026-04-08T16:48:45.527459Z

so then LLM lives in a fast feedback loop where it produces code, it gets executed, then LLM gets results fed back to it with clear explanations on what needs changed

yogthos 2026-04-08T16:49:40.343899Z

I did post some screenshots of the UI in the #ai-assisted-coding channel

yogthos 2026-04-08T16:51:33.909609Z

I basically have a declarative graph for the execution flow, and then have the llm implement each step in the graph as an isolated component which gets resources and state, and produces a new state, then the workflow engine examines it and dispatches to the next node in the graph

2026-04-08T16:59:16.757939Z

how do you draw that graph? xyflow/react/uix? whats the ui stack

yogthos 2026-04-08T17:02:07.987469Z

I used react flow

πŸ‘ 1
JAtkins 2026-04-08T22:11:19.921529Z

> I'm really surprised this sort of stuff isn't standard in tools like claude code, it would make so much more sense to load the project into a graph, and then work against that instead of grepping files constantly Same for humans. But we end up grepping about quite often too lol. I wonder if this could be made useful for editor use as well

yogthos 2026-04-08T22:13:10.314489Z

oh I imagine yeah, IDEs do some of this stuff already, but you definitely could do a lot more with a logic engine

yogthos 2026-04-08T22:13:28.648909Z

I wonder if it's just that most people aren't aware these tools exist and what you can do with them

JAtkins 2026-04-08T22:15:03.425149Z

I think IntelliJ recently added structural search. That's the first I’ve ever seen from editor land.

yogthos 2026-04-08T22:17:11.835819Z

and IDEs have had stuff like find usages for a while, so the work of building the graph was already being done, it's just the step of feeding this graph into a logic engine that's been missing

lread 2026-04-08T19:09:30.288839Z

Pomegranate 1.3.26 - A sane Clojure API for Maven Artifact Resolver + dynamic runtime modification of the classpath https://github.com/clj-commons/pomegranate/blob/master/CHANGELOG.adoc#v1.3.26 (the first is significant, so we bumped from v1.2 to v1.3) β€’ Support reporting of problem details as per RFC 9457. This means we now use HttpTransport instead of HttpWagon by default, and includes upgrading the dependency on org.apache.maven.resolver/maven-resolver-transport-file and related libraries (https://github.com/clj-commons/pomegranate/pull/233) (thanks @tcrawley! ) β€’ Override maven dep of org.codehaus.plexus/plexus-utils to address CVE-2025-67030 (https://github.com/clj-commons/pomegranate/issues/234) β€’ Review dependencies, remove redundant, explain unreferenced by code (https://github.com/clj-commons/pomegranate/issues/237), (https://github.com/clj-commons/pomegranate/issues/239) β€’ Fix scm connection urls in pomegranate’s pom (https://github.com/clj-commons/pomegranate/pull/229) (thanks https://github.com/frenchy64) Pomegranate is one of the many projects under the loving care of https://github.com/clj-commons. Drop by #pomegranate for chat/support.

2
5
2
πŸŽ‰ 5
dan.lentz 2026-04-08T20:16:34.837649Z

ordered-collections 0.2.0 Fast, modern, ropes and ordered collections that do more than sort. - Rope β€” A persistent, vector-like sequence type for structural editing. O(log n) concat, split, splice, and insert. 10–5000x faster than PersistentVector at scale. - Set algebra β€” 15–57x faster than sorted-set, 7–42x faster than data.avl, 3-20x faster than hash-set via parallel fork-join. O(log n) positional access (nth, rank, median, split-at), floor/ceiling navigation, and structure-sharing subranges. - 11 collection types β€” ordered-set, ordered-map, rope, interval-set, interval-map, range-map, segment-tree, priority-queue, ordered-multiset, fuzzy-set, fuzzy-map - Parallel fold β€” tree-based r/fold on all collection types - O(log n) positional access β€” nth, rank, median, percentile, split-key, split-at - Navigation β€” nearest (floor/ceiling), subrange with structure sharing - EDN tagged literals β€” round-trip serialization for all types - Primitive-specialized nodes β€” Long and Double key variants for 15–25% faster numeric workloads Plus: - Interval sets/maps β€” O(log n + k) overlap queries - Range maps β€” non-overlapping regions with automatic carve-out (Guava TreeRangeMap semantics) - Segment trees β€” O(log n) range aggregation with any associative operation - Fuzzy sets/maps β€” nearest-neighbor lookup by distance - Priority queues and multisets All 11 types share parallel r/fold, reduce, EDN tagged literals, Java serialization, and full Clojure/Java collection interface compliance. [com.dean/ordered-collections "0.2.0"] GitHub: https://github.com/dco-dev/ordered-collections

πŸ‘€ 12
πŸŽ‰ 17
2026-04-11T15:52:12.588559Z

And something like β€œ(string-rope-by :line ….” Could maybe implement alternative indexing.

2026-04-11T15:54:32.138749Z

That's just an idea - there are significant hurdles to implementing it I think

2026-04-11T16:05:50.373369Z

I'm getting a lot closer to a 0.2.1 with some nice cleanups, optimizations, and specialized ropes. The index stuff is interesting but for another day

πŸŽ‰ 1
dan.lentz 2026-04-25T15:06:38.444059Z

@smith.adriane i know my re-seq performance on the string rope is weak though. (that was your use-case, right?)

dan.lentz 2026-04-25T15:08:06.543389Z

also, did you see my Zorp's Guide? lol https://github.com/dco-dev/ordered-collections/blob/master/doc/zorp-example.md

phronmophobic 2026-04-25T18:53:11.129859Z

I use https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Matcher.html#find(), but I think it's the same idea. I don't think it's a performance bottleneck though.

phronmophobic 2026-04-25T18:54:22.184139Z

For my code editor, I need some way to slice by several different index types, https://clojurians.slack.com/archives/C06MAR553/p1775779888581529?thread_ts=1775679394.837649&cid=C06MAR553

2026-04-25T18:55:53.400679Z

i touched on that idea. code points in the string ropes 🀒 and arbitrary extraction from byte buffers

phronmophobic 2026-04-25T18:56:26.002849Z

For working with tree-sitter and rendering text via skia, it's also important to be able to grab byte array slices by byte index (eg. grab all bytes between 121-154).

2026-04-25T18:56:47.761689Z

we do that

phronmophobic 2026-04-25T18:56:56.632889Z

Surprisingly, indexing by code point hasn't really been useful.

2026-04-25T18:58:23.646659Z

utf8 is a miricle and we should all cut our losses and say thats the best idea we’ve got

phronmophobic 2026-04-25T18:58:41.622259Z

Yea, my code editor only supports utf-8

phronmophobic 2026-04-25T18:59:02.876509Z

I looked at the docs and it didn't seem like the same rope could be indexed via bytes, code unit, grapheme cluster, line, etc.

2026-04-25T18:59:55.936349Z

not yet. i don’t even know what the hell a grapheme cluster is yet

2026-04-25T19:00:38.931129Z

i have a bunch of ideas about code unit and denotational semantics

phronmophobic 2026-04-25T19:01:03.587929Z

I did make a video about all of these things, Designing Clobber: A Deep Dive into a Clojure-Powered Editor for Text and Code https://youtu.be/kRd4JYIiWb0?si=u3hd5dKHuysTfVRm

phronmophobic 2026-04-25T19:01:11.367379Z

Happy to answer any questions as well.

πŸ’― 1
phronmophobic 2026-04-25T19:03:44.229829Z

A grapheme cluster is a "user perceived character". Multiple code points can be combined into a single visual symbol. The unicode standard includes tables and rules for how to do it. Programmatically, you can use icu4j's https://srl295.github.io/icu-docs/apidoc/released/icu4j/com/ibm/icu/text/BreakIterator.html

2026-04-25T19:18:59.205089Z

i guess one question before i continue I keep getting up on signed versus unsigned semantics with byte rope I am very unused to signed. I should have both, right?

2026-04-25T19:19:10.383829Z

*hung up

phronmophobic 2026-04-25T19:20:12.190169Z

I'm just passing them along to these native libraries, so I just need a byte-array

2026-04-25T19:20:33.356339Z

oh we do that

phronmophobic 2026-04-25T19:20:55.106979Z

I don't think there's any reason to provide unsigned semantics, since there are appropriate functions that work with Java's byte and byte-array types.

2026-04-25T19:22:24.577949Z

i keep running into where i want to

phronmophobic 2026-04-25T19:23:27.054069Z

I'm curious what some example use cases might be

phronmophobic 2026-04-25T19:23:53.654579Z

where you wouldn't just use byte and byte-array

2026-04-25T19:26:12.836669Z

i mean, you are very likely right. i have another library where i fought an existential battle to get the ideas right. The whole thing was founded on the pain points with unsigned arithmatic on the JVM https://github.com/danlentz/clj-uuid

2026-04-25T19:27:18.222549Z

maybe this question is just PTSD

phronmophobic 2026-04-25T19:29:44.571829Z

also, it's probably easier to just give users byte-arrays and let them decide how to deal with them than try to guess what they need

dan.lentz 2026-04-25T19:30:53.566919Z

thats a good architectural boundary

dan.lentz 2026-04-25T19:31:27.952689Z

i love those

dan.lentz 2026-04-25T19:32:54.443919Z

however

dan.lentz 2026-04-25T19:33:30.396219Z

materialization costs a lot. Doing it within the rope is free

dan.lentz 2026-04-25T19:42:12.643249Z

no maybe i understand

dan.lentz 2026-04-25T19:43:59.750849Z

tbd

dan.lentz 2026-04-25T19:46:08.953019Z

ropes are not easy

dan.lentz 2026-04-25T20:03:14.552679Z

if you have ideas def add issue on github i want to work on this more

dan.lentz 2026-04-09T11:07:53.126429Z

i go into more detail in the documention theres an overview you can look at here. Ropes are more efficient for splitting, joining, splicing, concatting, etc. CLJS doesn't supported multithreaded and also would be a significant rewrite so i'm not sure theres a lot of motivation

dan.lentz 2026-04-09T11:09:00.663109Z

i also ship a full set of benchmarks with it

dan.lentz 2026-04-09T11:18:22.999229Z

yes. for <10 items Persistent Vector is faster (because at that size we are just a PersisitentVector wrapped in a one leaf tree.). Experimentally i found a chunk size invariant of 128-256 elements was reasonable. So, you won't see any advantage before that.

πŸ‘ 1
xificurC 2026-04-09T11:25:40.580319Z

motivation for cljs support is for the user, if one wants to write portable code. Today one can use the built-in data structures and functions working with those will work on cljs

dan.lentz 2026-04-09T11:34:59.425449Z

y, thats true. I might take a look at it at some point. and babashka. But, because JS has such different OO design and Performance characteristics than the JVM it would be a significant rewrite

xificurC 2026-04-09T11:37:11.475729Z

definitely, it's not a cheap task

dan.lentz 2026-04-09T12:37:31.713099Z

Otherwise, for context, here is the design rationale https://github.com/dco-dev/ordered-collections/blob/master/doc/concept/concept.md

2026-04-09T13:23:12.928009Z

Yeah the main motivation for my Rope in particular is the fact that it implements CharacterSequence which allows using them with Regex

2026-04-09T15:12:52.249289Z

Ya I’m definitely going to add specialized ropes for characters and bytes next

πŸ’― 1
2026-04-09T19:40:39.529339Z

ok this is fun (string-rope (slurp \"big-file.txt\"))

2026-04-09T19:41:21.057929Z

Yeah it's great!

dan.lentz 2026-04-09T23:31:01.875039Z

performant string rope not as easy as i thought it would be i may have to adjust my claim that ropes are "only 400 lines more code" I'm at a performance crossover at length of ~1K right now and after that we start to quickly dominate. Is that the right target? maybe try to treat smaller ones as regular strings under the hood?

dan.lentz 2026-04-09T23:32:47.890609Z

thats just a ballpark i'm working on improving benchmarks

phronmophobic 2026-04-10T00:11:28.581529Z

Depends on your use case. For a code editor, I think it's more important that it works for large code files than being optimized when used for small files. It's also really important to be able to slice and index code in various ways: β€’ by byte β€’ by code unit β€’ by grapheme cluster β€’ by line β€’ by line and row (eg. grab lines 126-186 and only include columns 20-120) You also want to be able to keep track of offsets for various things that shift as you insert and remove text (code mirror calls them https://codemirror.net/examples/decoration/). This is important for stuff like styled text, inline code evaluation, syntax highlighting, auto complete placeholders, etc.

2026-04-10T02:19:01.613519Z

this is only very early and not fully reiewed but i’m making progress

dan.lentz 2026-04-10T03:45:48.376399Z

strings first

dan.lentz 2026-04-10T03:53:36.466359Z

i'm sorry i'm on two different laptops. i'm the hat and rabbit

πŸͺ„ 1
πŸ‡ 2
🎩 2
dan.lentz 2026-04-10T04:21:57.246149Z

i love this its fascinating. plus ropes are so tactile and fun to play with

2
dan.lentz 2026-04-12T13:18:52.019769Z

If you're interested this is where we stand performance wise now if you're interested. The numbers are looking even more compelling

dan.lentz 2026-04-12T15:05:19.166369Z

i wrote up the benchmarking methodology here: https://github.com/dco-dev/ordered-collections/blob/021-specialized-ropes/doc/benchmarks.md

dan.lentz 2026-04-10T16:47:28.191759Z

do byte-ropes want to have signed or unsigned semantics?

2026-04-10T16:49:36.431549Z

well all java primitives are both

2026-04-10T16:49:52.858999Z

that is, they are treated as signed but you can specifically do unsigned ops

dan.lentz 2026-04-10T16:51:55.475519Z

ok y. i was thinking of it too much like a vector of ints

dan.lentz 2026-04-10T16:54:36.261429Z

the specialized-ropes are really just a "chunk" protocol over strings, vectors, and byte[]

dan.lentz 2026-04-10T17:02:20.643299Z

or whatever else we want

dan.lentz 2026-04-10T17:15:11.676599Z

it all abstracts away to the actual rope.

phronmophobic 2026-04-10T17:24:05.227459Z

Rather than working with byte arrays directly, it might be interesting to use dtype-next's containers as nodes. Then you can work with byte arrays or off heap memory. This could be especially useful if you are using the rope alongside tree sitter or some other native library.

dan.lentz 2026-04-10T21:30:55.366899Z

wow

dan.lentz 2026-04-10T21:53:19.360619Z

its hard though

dan.lentz 2026-04-10T21:55:25.286269Z

i need to read more about the gc maybe

dan.lentz 2026-04-10T21:59:26.126379Z

i support bare chunks internally so that eliminates pressure to optimize the rope for small sizes. we should be about the equal performance until we blast off

dan.lentz 2026-04-10T22:07:27.670029Z

my wife says if i say "rope" or "chunk" one more time i have to go sleep in the tree

πŸ˜„ 3
dan.lentz 2026-04-10T22:09:22.871829Z

i think it wouldn't be weight balanced anymore

phronmophobic 2026-04-10T22:18:06.992959Z

Have you read much of the stuff on how clojure's persistent data structures work? I think that's what Bifurcan's rope is based on.

dan.lentz 2026-04-10T22:21:36.791979Z

well bagwell and there's an excellent blog series on the persistent vectors. i did a ctrie project in common lisp, so there was a lot of that type of thing. but i'm sure there may be things i've forgotten to remember so i'm open to pointers

phronmophobic 2026-04-10T22:24:02.524119Z

I thought part of the optimization for these data structures was using 32 way branching and chunk sizes that are good sizes for CPU caching. Is your library doing something similar?

dan.lentz 2026-04-10T22:24:31.051229Z

no. its a binary tree

dan.lentz 2026-04-10T22:24:49.115579Z

however, the set algebra is work-optimal

dan.lentz 2026-04-10T22:25:16.783489Z

and its an amazing workhorse for implementing arbitrary concrete collection types

dan.lentz 2026-04-10T22:25:48.760029Z

i beat the built in hash set down to very small sizes for set algebra

phronmophobic 2026-04-10T22:26:47.266729Z

It was my impression that binary trees aren't very efficient on modern CPUs and that is solved by using a higher branching factor.

dan.lentz 2026-04-10T22:29:51.228399Z

well, its a really good question for sure. i don't know if there is any literature that supports the idea that set ops (union intersection etc) can be done more efficiently than split-join binary tree with fork join parallelism. with or without memory locality its just the optimal algorthm.

dan.lentz 2026-04-10T22:31:00.237529Z

now, proving that is way way above my pay grade. but i have links in the references

dan.lentz 2026-04-10T22:32:09.912219Z

there are always trade offs. I thought ctries would be great then i went back to ths

dan.lentz 2026-04-10T22:32:19.083559Z

they have 32 branching factor

dan.lentz 2026-04-10T22:32:45.784199Z

its basically a hash map with optomistically parallel inserts.

phronmophobic 2026-04-10T22:33:11.491059Z

well, I believe Bifurcan's rope is more similar to clojure's data structures. It might be interesting to see how it compares.

dan.lentz 2026-04-10T22:33:39.603579Z

for sure it is a nice library.

dan.lentz 2026-04-10T22:34:13.597939Z

and thats helpful feedback i defintely will

phronmophobic 2026-04-10T22:35:50.535799Z

It also keeps track of code points for indexing and slicing. I've been meaning to try to update it to track code units, grapheme clusters, and newlines.

dan.lentz 2026-04-10T22:36:39.316309Z

i haven't looked at those but the hope is they fit into my Chunk protocol and are trivial to add

dan.lentz 2026-04-10T22:37:44.103179Z

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Rope Chunk Protocol
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defprotocol PRopeChunk
  "Chunk abstraction allowing the rope kernel to be generic over chunk type.
  Extended to APersistentVector (generic rope) and String (string rope).
  Protocol dispatch cost (~2-3ns) is comparable to the type-hinted interface
  calls it replaces."
  (chunk-length [c] "Number of elements/characters in chunk")
  (chunk-slice [c start end] "Subrange [start, end)")
  (chunk-merge [c other] "Concatenate two chunks")
  (chunk-nth [c i] "Element/character at index i")
  (chunk-append [c x] "Append a single element/character")
  (chunk-last [c] "Last element/character")
  (chunk-butlast [c] "All but last element/character")
  (chunk-update [c i x] "Replace element/character at index i")
  (chunk-of [c x] "Create a new single-element chunk of the same type")
  (chunk-reduce-init [c f init] "Reduce over elements/characters with init value.
    When f returns (reduced x), stops and returns @(reduced x).")
  (chunk-append-sb [c sb] "Append chunk contents to StringBuilder")
  (chunk-splice [c start end replacement] "Replace [start, end) with replacement chunk (may be nil for deletion)")
  (chunk-splice-split [c start end replacement half]
    "Like chunk-splice but returns [left-chunk right-chunk] split at position
    half in the spliced result. Avoids building the full intermediate chunk.
    Used by the fused splice overflow path."))

phronmophobic 2026-04-10T22:39:04.707859Z

That would be awesome.

dan.lentz 2026-04-10T22:39:49.014919Z

wip

phronmophobic 2026-04-10T22:41:51.630369Z

Working with Bifurcan's Java code is a pain (for me). I assume an idiomatic clojure version would be a lot more flexible. I wouldn't be surprised if it ends up being faster, just because it's easier to change.

dan.lentz 2026-04-10T22:45:17.237439Z

y, thats the entirety of the surface area for defining a new species of rope. And wrap it in a new collection deftype

phronmophobic 2026-04-10T22:46:34.867249Z

I imagine you would need to have additional protocols if you want to index/slice by different indices (eg. byte, code unit, grapheme cluster, line, etc)?

dan.lentz 2026-04-10T22:47:46.416839Z

yes i havent even started to think of what that looks like tbh

dan.lentz 2026-04-10T23:01:13.539429Z

but, you can openly extend this protocol over any concrete type you want. so maybe adding some type of index indirection to the protocol could get us there. any problem in computer science can be solved with another layer of indirection, except for the problem of too many layers of indirection, as they say

1
dan.lentz 2026-04-10T23:49:46.773939Z

https://github.com/danlentz/cl-ctrie

dan.lentz 2026-04-10T23:50:22.583259Z

went nowhere but superb exercise

dan.lentz 2026-04-10T23:51:29.701289Z

i went way, way, too general. which was fun, but no hope of being performant

dan.lentz 2026-04-10T23:52:48.909829Z

"Pandoric Objects". lol. we were all young once

dan.lentz 2026-04-11T00:02:02.050389Z

as far as this library, i needed some of this for work. so i donated https://github.com/danlentz/clj-wbtree and released it through them

dan.lentz 2026-04-11T00:05:45.266879Z

this way i can get paid to work on it (although patience may be wearing thin lol)

dan.lentz 2026-04-11T00:11:24.106359Z

i see it as just supporting "open source" in some minor way and they're aligned with that so all good

dan.lentz 2026-04-11T00:20:01.748949Z

we've been using it in production for like 8 years

dan.lentz 2026-04-11T00:33:20.474099Z

my early version had a dynamic balance parameter where you could turn into a red-black tree or any other balance strategy by binding n-join but it turns out all the other ones suck

dan.lentz 2026-04-11T00:47:17.704399Z

@smith.adriane ctrie does have a memory-mapped on-disk persistence model, with hand written garbage collector so thats why i started thinking of all this.

dan.lentz 2026-04-11T00:49:26.153129Z

it also could be a lightweight in memory object of function thats where i went wrong. but it was fun times

dan.lentz 2026-04-11T00:54:40.573109Z

*went wrong with ctrie i mean.

dan.lentz 2026-04-11T00:57:01.468169Z

i'm still pushing for generality+performance but this is a better approach

dan.lentz 2026-04-11T01:01:03.618499Z

i'm takin another swing at it, baby

dan.lentz 2026-04-11T01:07:03.128029Z

(what i've just explained to my wife about the new AI bills)

dan.lentz 2026-04-11T02:28:23.195559Z

back to business, papering over losses at small sizes by supporting bare chunks makes us perflormant at every cardinality

dan.lentz 2026-04-11T02:40:26.161899Z

that resolves the other guys question about if they're perforant at <10 nodes (almost)

2026-04-08T20:23:59.352329Z

hey @suskeyhose haven't you talked about wanting a ropes library?

phronmophobic 2026-04-08T20:25:06.442329Z

https://github.com/IGJoshua/ropes

πŸ˜„ 1
2026-04-08T20:25:13.492709Z

Yea I wrote one lol

2026-04-08T20:25:15.807209Z

but this is cool!

βž• 1
2026-04-08T20:27:21.588819Z

haha crap, i'm late to the party. yes, this is also very cool

phronmophobic 2026-04-08T20:28:18.917709Z

It might be interesting to compare with options from https://github.com/phronmophobic/bifurcan

dan.lentz 2026-04-08T20:44:29.706369Z

yeah. that wasn't in my radar but it should have been thanks

dan.lentz 2026-04-08T20:45:15.043669Z

good for benchmark and equivalence testing

dan.lentz 2026-04-08T22:07:48.080379Z

i could do a string specialized rope for next version. at some point i just had to cfut this off and get it out the door

πŸ‘ 1
phronmophobic 2026-04-08T22:08:56.901829Z

yea, looks great!

phronmophobic 2026-04-08T22:10:02.620779Z

I've been using a fork of Bifurcan's rope for my IDE's text editor, https://github.com/phronmophobic/clobber. If you want to chat about Rope use cases, I would be happy to.

phronmophobic 2026-04-08T22:12:56.674909Z

For code editing, it's really nice to have slicing/indexing by: β€’ byte (for compatibility with tree sitter) β€’ code unit (for compatibility with java strings) β€’ grapheme cluster (for editing text) Surprisingly, I haven't found a need to slice/index by code point.

dan.lentz 2026-04-08T22:14:03.651669Z

rope bro's. ya i think i could learn a lot. i'm about done for today but dm me or i'll dm you

πŸ‘ 1
xificurC 2026-04-09T06:51:52.941849Z

> 10–5000x faster than PersistentVector at scale. for what operations, all of them? What does "at scale" mean, e.g. for <10 items persistent vector is faster? Do you plan cljs support?

dan.lentz 2026-04-17T18:44:16.240849Z

⏺️ ordered-collections 0.2.1 is out A follow-on to 0.2.0 focused on specialized ropes and a performance pass. New collection types - string-rope β€” persistent chunked text, implements CharSequence so it drops into re-find, clojure.string, http://java.io.*. EDN tag #string/rope. ~130Γ— faster than String on structural edits at 500K chars. - byte-rope β€” persistent chunked binary with unsigned [0, 255] semantics and streaming MessageDigest. EDN tag #byte/rope. ~128Γ— vs byte[] on remove at 500K. Rope kernel - One kernel now drives all three variants via a small PRopeChunk protocol. - Flat-mode for small ropes (≀ 1024 elements live as a bare PersistentVector / String / byte[], no tree). - Monomorphic nth / reduce hot paths per variant (~2–2.5Γ— faster). Tree kernel tune-ups - Primitive rank for long-ordered- / string-ordered-. - O(n) bulk build for sorted-disjoint range-map input. - Non-allocating .iterator() for OrderedSet / OrderedMap (2Γ— vs sorted-set, 3.6Γ— vs data.avl). Bug fixes. Primitive specialization no longer silently downgrades on conj. Various small StringRope / ByteRope contract fixes (empty fold, non-integer keys, InputStream bounds, surrogate-pair chunking). No breaking changes. 695 tests, 471K assertions. Full details: https://github.com/dco-dev/ordered-collections/blob/master/CHANGES.md Β· Numbers: https://github.com/dco-dev/ordered-collections/blob/master/doc/report.txt

πŸŽ‰ 1
phronmophobic 2026-04-17T18:46:14.643009Z

It may be worth a post in #releases

dan.lentz 2026-04-17T19:08:20.603549Z

yup done. you will have to take a look at byte-rope. I still had to make some decisions on semantics that I'm not sure of. The way strings should behave seemed clearer.

dan.lentz 2026-04-17T20:04:12.132489Z

the byte ropes and string ropes were a learning experience. You can be elegant and pass through ~5 layers of protocols, but leave a lot of easy performance wins on the table. Or you can be fast. I worked hard to factor as much for simplicity as possible, but where the two ideas were in conflict i went for fast.

dan.lentz 2026-04-17T20:10:00.067539Z

as far as semantics, this was an example where i just had to pick a lane. maybe it should have binary protocols for both signed and unsigned semantics?

;; ByteRope β€” binary protocols, streaming digest
(def packet (oc/byte-rope [0x48 0x45 0x4C 0x4C 0x4F]))
(oc/byte-rope-get-int packet 0)  ;=> 1212501068