Qi Meeting Jan 23 2026 - drym-org/qi GitHub Wiki
The Stars Align
Qi Meeting Jan 23 2026
Adjacent meetings: Previous | Up | Next
Summary
We planned the next Qi release, provisionally targeting the spring equinox.
Background
Last year, we did a lot of very cool work on Qi and discussed a lot of neat ideas, but much of it is still in unmerged branches or is otherwise unresolved.
Release Planning
We felt that collecting the outstanding work into an Org file and filing it under release and post-release items would help focus our efforts.
What Should Be in the Release?
We inspected the curated "Next Steps" section from the most recent meeting notes and converted it into an Org file, and then went through the items in order, filing them appropriately after discussing them.
We came up with this final list of todos:
* Qi todos [0/24]
** Release
- [ ] Deforestation [Dominik et. al.]
- [ ] Define the `define-producer`, `define-transformer`, and `define-consumer` interface for extending deforestation (also encapsulating both naive and stream semantics in the definition), and re-implement existing operations using it.
- N.B. consumers complexity/time required currently unknown
- [ ] Implement the remaining producers and transformers in `racket/list` for `qi/list`.
- [ ] Decide on whether there will be any deforested operations provided in `(require qi)` (without `(require qi/list)`)
- [ ] Review which `racket/list` forms are actually needed in `qi/list`
- [ ] Come up with a good way to validate the syntactic arguments to `range` using contracts.
- Don't like it, but let's do it!
- [ ] Why is `range-map-car` slower against Racket following the Qi 5 release?
- [ ] Decide on appropriate reference implementations to use for comparison in the new benchmarks report and add them.
- we formerly compared Qi and Racket
- now we compare deforested vs non-deforested
- comparing standard functional operations (range, map, filter, foldl) in Racket vs Qi
- [ ] Write up Qi's theory of effects notes + docs PR, including accounting for binding rules. [Sid]
- bindings optional for this release
- [ ] Write up the operational semantics of Qi in the docs [Eutro]
- [ ] Develop a backwards-incompatibility migration tool using Resyntax, to be used in the next Qi release. [Sid/Jacqueline]
- [ ] Incorporate "bingdi" compact currying syntax [Noah/Sid]
** Post-release
- [ ] Incorporate Chai into Qi. [but message Sam]
- [ ] Write a proof-of-concept for implementing code generation from abstractions of "flow" and "connective tissue" that are set by a context parameter. [Sam]
- [ ] Publish `qi/class` in some form. [but message Jair]
- [ ] Remaining formalization of Qi's semantics using Redex.
- [ ] Add bindings and effects to the Redex semantics (including: ensure that test cases validate that scoping rules are purely lexical and not dynamic) [sorta blocked on theory of effects notes + PR]
- [ ] Incorporate effects and bindings into Qi's pen-and-paper semantic model. [Sid]
- [ ] Implement a compile-time table of known arities and add an entry to it as part of `define-flow`.
- [ ] Ready the [inlining PR](https://github.com/drym-org/qi/pull/200) to be merged and tag for code review. [blocked on issues noted in the PR]
- cross-module inlining
- mutually recursive functions
- concerned re: semantics of substitutions [might break things and we aren't sure]
- can't add a new scope to an expression that has host expressions in it [due to some interaction with syntax spec] [this is mostly on us, but we do need Michael]
- [ ] Write phase 1 unit tests for inlining.
- [ ] Deforestation
- [ ] Attach a `deforested` syntax property in the deforestation pass, and use it in compiler rules tests (instead of string matching). [nice to have -- big and doesn't affect release]
- [ ] Improve `qi/list` and deforestation testing by writing a macro to simultaneously test the expansion and the semantics. [nice to have --- we do test these independently, but not together]
- Currently have 3 separate places for tests --- hard to keep straight
- Need a way to quickly add to the test suite once a new stream component is ready
- [ ] Investigate whether the deforested operations could be expressed using a small number of core forms like `#%producer`, `#%transformer`, `#%consumer`, and `#%stream`.
- [ ] Start organizing `qi-lib` into `qi` and `qi/base` collections
- this will be informed by further reduction of the core language; for now, there's not much value in doing this
- [ ] Implement DAG-like binding rules for branching forms [[Syntax Spec parallel binding-spec PR](https://github.com/michaelballantyne/syntax-spec/pull/85/)][Eutro and Michael]
- blocked on needing to work out exactly how to implement it
- the implementation in the PR is just a proof-of-concept and not usable as is
** Done
... which is now housed on the wiki at Qi 6.
The names in [] on some of the items are the people who have been taking the lead on these tasks and are the point people for it (but it's not consistently used). As we like to say, Qi is an ABE project and not a startup, so the semantics of "assigning" tasks are more just tacit recognition, involving interest, service, fun, community, value, and such things, rather than obligation.
The annotations below each task describe their current status and any blockers.
When Should We Release?
Between the spring equinox and the summer solstice, we chose to ambitiously target the equinox, and the closest Friday to that is March 20, so, about 8 weeks from now!
It's a tidy amount of work, but we felt that it wasn't obvious that it wasn't doable, so that made it worth aiming for 😆.
At the same time, we identified a few unknowns, including the time required for the interface for defining stream consumers, and also the time required for design improvements like "bingdi" and the availability of Noah and Sid and other contributors --- of course, the proposed release timeline is provisional and would be informed by these factors!
Follow Up
We identified some follow-up items to notify contributors who were not present of our plans, in case they wanted to get something into this release as well.
Compact Currying Syntax
We also discussed Noah's proposal for compact closure syntax and its generalization enabling Qi to be used to operate both on values as well as on "flows as values."
The deeper (esp. category theoretic) implications are yet to be understood, but we investigated the initial proposed syntax in a Racket buffer to verify that there are no conflicts on the surface:
#lang racket
(require qi)
(define f (make-parameter +))
(~> (1 2 3) (clos (esc (f))) apply)
(~> (odd? 1 2 3) pass)
;=>
(~> (odd?) (pass) (_ 1 2 3))
;; required transformation for resyntax
(~> (odd? 1 2 3) pass)
;=>
(~> (odd? 1 2 3) (==* (pass) pass) apply)
;; or this?
(~> (odd? 1 2 3) (==* (pass) pass) (_ __))
;; Question: does apply need to work the same as (_ __)?
(~> (odd? 1 2 3) pass)
An Elegant Solution?
Somewhere in the course of release planning, we reckoned with these three items:
- We promised Jacqueline some time ago that, as part of the next release, we would explore Resyntax to ameliorate any backwards incompatibilities.
- Sid has lamented that there is no symbolic name for the identity flow,
_, and hoped to be able to usepass, which is already used as the name of the values-oriented analogue offilter. - Noah proposed the compact currying syntax some time ago ("bingdi," aka "yin/yang symmetry") that generalizes into an alternative to bindings in many cases.
For the first, in order to usefully use Resyntax, we needed a nice instance of a backwards incompatibility.
We had considered the second item as serving this purpose before. But changing the name of the pass form (which is of course backwards-incompatible) for mostly aesthetic reasons seemed gratuitous.
We discussed this in the meeting and considered that there would be no contradiction if pass, on its own, as an identifier, were interpreted as something like (pass identity) (more precisely, (pass values)), which is equivalent to simply values and to _. But unfortunately, pass on its own, as an identifier, currently uses the "control input" form of this values-filtering flow, which expects the first flow input to be the predicate function to filter by (analogous to the "control" input in a digital circuits like multiplexers). Before the introduction of bindings to the language, this "control" form of flows was the only way to use values produced by flows as flows, at runtime.
But discussing further, we felt that now that we have bindings, using this "control" form is already non-idiomatic, and we felt it's more a "Qi 2" or "Qi 3" pattern than a "Qi 6" one. And we soon realized not only that but that Noah's proposal (the third item above, which we had already identified for inclusion in this upcoming release) provides yet another and more elegant way in which this could be accomplished. The inclusion of this in the upcoming release provides even more justification for deprecating the use of the "control input" form of pass, and using this name now for the identity/do-nothing flow, which simply "passes through" all values.
Which also, conveniently, gives us the "nice backwards incompatibility" that we needed to cut Resyntax's teeth on as far as Qi is concerned, and with the incompatibility being only mild (specifically the identifier/control form of pass, which is already considered non-idiomatic).
[Although, looking again now, I see that the proposal for compact closure syntax uses the identifier forms of combinators to "lift" operations to produce flow values, in which case pass and not (pass) would lift to flow values (e.g., (~> (odd?) pass (_ 1 2 3)) ;=> 1, 3. So perhaps pass here can't be _, after all, unless we use allow for it. Another option is to use (op) to uniformly mean "lift to flow values," both for ordinary functions as well as Qi combinators. But in that case, making a special exception for pass to stop supporting the "control input" syntax, when other combinators would continue to do so (as (op) is unused syntax today), seems undesirable.]
Syntax Quibblings
Hashing out Hash Syntax
Sid recently adopted Dominik's suggestion to use explicit grouping of keys and values in hash syntax for #lang raqit. In discussion today we felt it would also be OK to use {a ...} for sets now, and explicitly require parentheses for key-value pairs if a hash is intended. Otherwise, something like {['a 1] ['b 2] ['c 3]} becomes a set of lists. We felt that this constraint was acceptable, and preferable to using #{} for sets.
Let Syntax Parse to Do the Hard Work
On the subject of brackets, we discussed "mathematical angle brackets" (⟨ and ⟩) and "double-angle brackets" (⟪ and ⟫), and considered that these additional delimiter types in the unicode space could be used for something, too.
Perhaps closed ranges? But for ranges, it might just be easiest to indicate them using .., like (.. low high step). At that point, it could be even more intuitive to use infix syntax for .., as Rhombus does. Could we use this in infix form even in Racket/Raqit?
Dominik shared a trick he has grown quite fond of in recent times --- using the expander --- and Syntax Parse --- to do the hard work that we might otherwise have expected the reader to do. Syntax like [1 .. 10] (for a list containing the numbers from 1 to 10) seems like it would need to be handled by the reader, "looking ahead" when a [ is encountered to see whether it should generate a list or a range form.
But instead, the reader can simply generate "list-or-range" upon encountering a [. Then, the expander includes a list-or-range macro that produces either a list or a range, depending on a case analysis of the syntax pattern. This is an incredibly powerful technique because the reader is regular while the expander is context-free, and encoding patterns in syntax-parse is much easier than doing look-ahead searches in the reader.
So, actually, [low .. high] and [low low+step .. high] infix syntax in Raqit seems straightforward and would be "cute." Like Rhombus, it could generalize to other types, depending on the reader syntax used, e.g., (low .. high) could denote a lazy (sequence or stream) range.
Syntax isn't the focus of #lang raqit (which is DSLs, esp. Qi), but it's worthwhile to adopt good syntactic ideas if they are easy to implement.
Rabbit Holes
Emacs
Eutro has been working on Xup again lately. She's actually managed to make improving her Emacs config a dependency of her coursework next term (to format math and such things), like a true boss. So she's going back down the Emacs rabbit hole, full steam ahead, and hopes to have it ready for prime time in the near future. Dominik and Sid cautioned that "making good progress" isn't a sure indicator of imminent emergence from a rabbit hole, as the rabbit hole could be a branching tree of rabbit holes, of potentially uncountable cardinality. Eutro agreed, and explained that she can see the light at the end of this rabbit hole, though she still couldn't be quite sure that she will actually end up going towards it!
Unable to bear being left out of Emacs rabbit holes, Sid took some time to document Elacarte this past week, as he felt it solves problems on the package distribution and installation side that complement Xup's approach on the configuration side. Eutro also agreed that they complement each other, but she needs the ability to support multiple concurrent cookbooks in Elacarte, which isn't quite supported yet. Sid plans to add this feature soon. 🐰
Next Steps
Please see Qi 6.
Attendees
Dominik, Eutro, Sid