# Effect composability: actual callstack vs syntactic scope

**URL:** <https://discuss.ocaml.org/t/effect-composability-actual-callstack-vs-syntactic-scope/17536>\
**Category:** Learning\
**Created:** [November 27, 2025, 12:45pm UTC](https://discuss.ocaml.org/t/effect-composability-actual-callstack-vs-syntactic-scope/17536 "2025-11-27T12:45:28Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![wokalski](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/wokalski/32/38_2.png) [@wokalski](https://discuss.ocaml.org/u/wokalski)\
**Post date:** [November 27, 2025, 12:45pm UTC](https://discuss.ocaml.org/t/effect-composability-actual-callstack-vs-syntactic-scope/17536/1 "2025-11-27T12:45:28Z")

</div>

We hit an issue mentioned in [The status of eio and effects composition - #19 by rand](https://discuss.ocaml.org/t/the-status-of-eio-and-effects-composition/15513/19) and [How to compose effect handlers with Eio](https://discuss.ocaml.org/t/how-to-compose-effect-handlers-with-eio/11279) and I’m just curious; do the OCaml maintainers see the fact that the scope of effect handlers is different than the syntactic scope as a problem? Or is it accepted as the de-facto design, we rely on the actual call stack and that’s it?

Actually maybe this is a wrong question; but seems especially relevant in the context of typed effects. This has to be addressed somehow one way or another.

---

<div class="post-metadata">

**Author:** ![contificate](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/contificate/32/4825_2.png) [@contificate](https://discuss.ocaml.org/u/contificate)\
**Post date:** [November 27, 2025, 3:23pm UTC](https://discuss.ocaml.org/t/effect-composability-actual-callstack-vs-syntactic-scope/17536/2 "2025-11-27T15:23:04Z")

</div>

I’m not expert on the topic, but I believe the direct implementation of delimited control is always taken as more flexible when you control the back-end (even if you add a form of static safety to their interface). For example, in Scheme, many libraries provide `shift/reset` - control operators proposed by Danvy and Filinski in their paper [Abstracting Control](https://dl.acm.org/doi/pdf/10.1145/91556.91622). In their paper, they specify that `shift`’s context is syntactically delimited by the enclosing `reset`. However, the vast majority of implementations (that I’m aware of) do it dynamically (e.g. using `reset` marks that delimited portions of the call stack). It alleviates the need for a static translation (e.g. conversion into CPS or similar) and allows you to link with library code that `shift`s to handlers the user installs.

I don’t see OCaml changing in this regard (implementation wise), even if features to provide effect safety are eventually provided. It’s a similar situation to exception handlers, really. You can imagine a system similar to Java’s `throws` clause that propagates information across to users. It’s safe in the context of Java code, but native JNI code can also raise exceptions (as can C code called from OCaml) that propagate to the VM.

Sorry if this reply misses the point, but I’m not sure if you’re proposing a design change to the implementation, the interface of effects (that builds a syntactic restriction above the feature), or the interface of Eio. It’s probably not controversial to say that OCaml’s “effect handlers” are an unsafe, limited, form of delimited control intended for writers of concurrency libraries (which are designed to be efficient, hence the one-shot restriction and - related - fact that captured continuations are not actually first class functions). The linked comment seems to implicitly assume that OCaml’s effect handlers are a feature intended for casual users (doing general programming), which I think is somewhat questionable at this stage.

* * *

I think a continuation of your thread about composing effect handlers with Eio would be interesting (and seemingly a specific library/interface concern). I don’t see the OCaml default changing, but I do think it’s interesting to consider how many runtime things play together. I don’t think OCaml has much opinion about which should be the library of choice (as it doesn’t provide a standard for it), so we’re still in a place where much is being worked out (?).

---

<div class="post-metadata">

**Author:** ![wokalski](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/wokalski/32/38_2.png) [@wokalski](https://discuss.ocaml.org/u/wokalski)\
**Post date:** [November 27, 2025, 3:45pm UTC](https://discuss.ocaml.org/t/effect-composability-actual-callstack-vs-syntactic-scope/17536/3 "2025-11-27T15:45:47Z")

</div>

I consider effects a feature of OCaml and as such an abstraction that I should be able to use. There’s even effect syntax now! I can’t seriously consider it an implementation detail for concurrency libraries.

If you think about writing programs that involve concurrency like this, effects, in my opinion should remain usable because they are a wonderful abstraction.

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [August 12, 2026, 10:41pm UTC](https://discuss.ocaml.org/t/effect-composability-actual-callstack-vs-syntactic-scope/17536/4 "2026-08-12T22:41:15Z")

</div>

@wokalski I gather that it’s too late by now, but the problem that you faced here with Eio has a solution in the just released [affect](https://discuss.ocaml.org/t/ann-affect-0-0-0-a-streamlined-and-natural-concurrency-model-for-ocaml/18451) concurrency model and library via the notion of asynchronous [call handlers](https://erratique.ch/software/affect/doc/Affect/Fun/Async/Call_handler/index.html). See [here](https://erratique.ch/software/affect/doc/cookbook.html#basics_effects) for an example.

It’s not exactly _syntactic scope_ in the sense that you need to manually plug your call handler when you do your [asynchronous function call](https://erratique.ch/software/affect/doc/Affect/Fun/Async/index.html#val-call), but once you have done that it respects the _function scope_: call handlers are inherited and composed with those of child asynchronous function calls. So these child calls will be subject to it unless their own call handlers override it.

That entails at least an operational reconciliation with what you’d expect from syntactic scope – i.e. no `Exception: Stdlib.Effect.Unhandled E` in a subcomputation of `f` if you plugged a handler for `E` when calling `f`.
