I am super excited with the direction that the nimony project is taken and super greatful that @arak and the team are taking the time to really think about the design of the language. I am not sure if this has been a consideration or discussed already but I would like to explore effect handlers in future nim versions. This is not a proposal, just a long term exploration suggestion that I think fits with Nim’s Zen in the sense that it could unify several different concepts of the language guided by first principles.
Since Nimony’s concurrency model will be based on continuations. a natural question is whether this same CPS substrate could support restricted, single-shot effect handlers as an extensible way to express future control-flow abstractions, without introducing new hard-coded language features or special cases in the core.
In Nim terms, only single-shot (affine) handlers are a plausible fit. They align with CPS-lowered concurrency, preserve a clear cost model, and match the “resume exactly once” structure already assumed by scheduling and task execution. Multi-shot handlers, by contrast, require continuation duplication or reification, introduce non-linear control flow, and make both performance and resource lifetime harder to explain. This would be a poor trade-offs for Nimony’s design goals.
The reasons I think this could be doable are:
3. Resumption corresponds to invoking that continuation exactly once, which is already the invariant assumed by Nimony’s scheduler and CPS lowering. This suggests that restricted effect operations and handlers could, in principle, be compiled using the same CPS machinery Nimony already relies on, rather than introducing a fundamentally new execution model. This machinery could be used by the compiler with little to none visibility to the user or at most by exporting an effect handle API with nice macros.
Potential future-proofing benefits
If explored carefully, such a restricted handler mechanism might:
These are possible benefits, not guarantees.
Risks and why this must remain exploratory
Experience from other ecosystems, such as OCaml 5, Koka, and Unison, recommend restraint. Effect handlers are most successful when narrowly targeted and when they remove more special cases than they add, shifting some complexity from the compiler into libraries while keeping the execution model comprehensible.
The appropriate goal, if this space is explored at all, would therefore be a minimal, explicit, single-shot handler model that generalizes Nimony’s existing CPS-based control flow only if it demonstrably keeps the compiler simpler and easier to extend over time.
The space has been explored 4~5 years ago.
It's definitely possible, it's already feasible today to do CPS as a library through Nim macros, the major hurdle iirc was dealing with type resolution which is a pain in macro.
See https://github.com/nim-works/cps (it's very messy but, you have rough examples).
I did an in-depth investigation of most languages here: https://github.com/mratsim/weave-io-research/blob/d12c0b5/research/README.md, feel free to add Koka, OCaml 5 and Unison.
And I wrote specs:
Yes I believe that says a lot about how the design of the ocaml 5 compiler emerged. I don’t have significant experience with ocaml but I suggest that representation and semantics were decisions taken within different constraints and levels of maturity. OCaml uses a stack-segment representation that could support continuation copying and multi-shot resumptions, yet the language deliberately chooses single-shot semantics. That choice was not forced by the implementation; it was made to preserve predictable performance, linear resource reasoning, and a simpler mental model for concurrency. From the outside it almost feels like the team regretted having too much power.
From first principles, CPS is neutral: it can support both single-shot and multi-shot continuations. The difference is that multi-shot always requires explicit duplication of control state, whether that state lives in stack frames or in CPS closures/state machines. CPS doesn’t remove that cost but makes it explicit.
Single-shot is often the better default even when multi-shot is technically feasible.
Framed this way, CPS keeps Nimony’s options open, while also making any move toward multi-shot a conscious, explicit trade-off rather than an accidental capability. One would have to buy into multishot at a cost, while most of the work needed for single-shot is already in the design decision to go for CPS . That is why I feel that AE could be idiomatic. It is not necessary a new feature as such, but more of a generalization of several features like suspendable functions and exceptions that if done right could simplify their implementation on a compiler level and potentially make other instances of that generalization simpler to implement in the future.
Like I said, this is not a feature request but more of an exploration. I believe that most of it could be implemented or emulated on a library level, but the real benefit I believe is potentially simplifying the compiler. It is not required for async/await, exceptions, and so on, these already work. Just food for thought