This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.

Title

Learning about OCaml Effects

Description

<img attachment="ocaml_logo.webp" align="right" caption="OCaml Logo">I don't program with OCaml. I never have. I have a good colleague who does, occasionally, write stuff in OCaml, and he sent me a bunch of links about OCaml Effects, starting with a discussion asking <a href="https://discuss.ocaml.org/t/are-we-rational-about-exceptions-and-effects/17111" author="olleharstedt" source="OCaml Community">Are we rational? About exceptions and effects</a>. The author writes, <bq>I was thinking about the fact that there’s no consensus about exceptions and whether to include them or not in a programming language. <b>Think about Go. They decided to not add support for exceptions. Did they cite any study to support this decision</b>, that supports the notion that exceptions in general lower the quality[1] of the ecosystem? Not that I know of. <b>Now OCaml goes in the opposite direction - adding more ways to jump around in the code, with effects. Also no studies, no experiments.</b></bq> This is an excellent point to make: we consider ourselves to be rational engineers and scientists but, very often, we nearly completely elide our reasoning for major decisions about architecture and functionality.<fn> From there, I was intrigued to learn more about this controversial feature in OCaml and I landed on the documentation page called <a href="https://ocaml.org/manual/5.3/effects.html" source="OCaml Manual">Chapter 12 Language extensions - 24 Effect handlers</a>, which summarizes its topic as follows, <bq>Effect handlers are a mechanism for modular programming with user-defined effects. Effect handlers allow the programmers to describe computations that perform effectful operations, whose meaning is described by handlers that enclose the computations. <b>Effect handlers are a generalization of exception handlers and enable non-local control-flow mechanisms such as resumable exceptions, lightweight threads, coroutines, generators and asynchronous I/O to be composably expressed.</b></bq> While I---someone who's been reading programming-language specifications for over 30 years---really like the sound of that, I'm forced to admit that most of the documentation, while comprehensible to someone versed in language constructs and terminology, serves as a perfect example of "why no-one uses OCaml." It is <i>dense</i>. Consider the following description of a concept like exception-handling, which many programmers can grok relatively easily, when it's been abstracted away into a generalized effect mechanism, described as follows, <bq>We run the computation <c>comp1 ()</c> under an effect handler that handles the <c>Xchg</c> effect with a continuation bound to <c>k</c>. Here <c>effect</c> is a keyword which signifies that the <c>Xchg n</c> pattern matches effects and not exceptions. As mentioned earlier, effect handlers are a generalization of exception handlers. Similar to exception handlers, when the computation performs the <c>Xchg</c> effect, the control jumps to the corresponding handler, and unhandled effects are forwarded to the outer handler. However, unlike exception handlers, the handler is also provided with the delimited continuation <c>k</c>, which represents the suspended computation between the point of <c>perform</c> and this handler.</bq> This is not to take anything away from the documentation---which is precise, indubitably accurate, extensive, and replete with examples---but you're not going to be onboarding any newcomers or dilettantes with this kind of stuff. As a sign that even OCaml community members are aware that this might be a problem, the page <a href="https://github.com/ocaml-multicore/ocaml-effects-tutorial" author="" source="GitHub">Concurrent Programming with Effect Handlers</a> offers another view on it. It describes OCaml effects as, <bq>An algebraic effect handler is a programming abstraction for manipulating control-flow in a first-class fashion. They <b>generalise common abstractions such as exceptions, generators, asynchronous I/O, or concurrency</b>, as well as other seemingly esoteric programming abstractions such as transactional memory and probabilistic computations. <b>Operationally, effect handlers offer a form of first-class, restartable exception mechanism.</b> In this tutorial, we shall introduce gently algebraic effect and handlers with gentle examples and then continue on to more involved examples.</bq> Don't get me wrong, I find reading about a generalized mechanism that collects all of the effect-ful mechanisms hard-coded into other languages <i>fascinating</i>. With effects, OCaml has sacrificed "elegance and approachability of the language" for "provability of the program". Even if the number of people who end up using this feature in OCaml amounts to a rounding error, I think that research into mechanisms like this is vital because it leads to improvements in other, more mainstream languages. Related to this all is a practical implementation using effects for a laudable goal: inversion of control and dependency injection<fn>, for which the approaches in OCaml are described in detail in <a href="https://gr-im.github.io/a/dependency-injection.html" author="" source="Grim's web corner">Basic dependency injection with objects</a>. It discusses two common approaches to DI in OCaml and then proposes a more practical alternative. On the effect-based approach, the author writes, <bq>an Effect system is often described as <b>a systematic way to separate the denotational description of a program, where propagated effects are operational “holes” that are given meaning via a handler</b>, usually providing the ability to control the program’s execution flow (its continuation), unlocking the possibility to describe, for example, concurrent programs.</bq> <bq>It’s quite amusing to see that <b>dependency injection and exception capturing can be considered two special cases of effect abstraction</b>, differing only in how the continuation is handled.</bq> Spoiler: the author ends up using objects rather than modules (weak type-inference support, overly verbose) or effects (weak type-inference support, complexity). <hr> <ft>Where the OCaml discussion centers on the rationality of how a working group chooses which features to include in a programming language, the article <a href="https://spf13.com/p/the-hidden-conversation/" source="spf13" author="Steve Francia">Why Engineers Can't Be Rational About Programming Languages</a> has a very good discussion about the general irrationality that influences choosing a programming language for a project. <bq>[...] <b>what struck me was how broken their reasoning was.</b> If they were making a logical argument, surely they would have considered Go and in doing so with their presented criteria they would have realized Go was a better option and, at the very least, refined their criteria. I pulled the VP aside after the meeting. <b>“Walk me through how you evaluated other language candidates,” I said. His face went blank. “We… didn’t really look at any others,” he admitted. “Everyone’s talking about Rust.” There it was: a 50 million dollar decision made on hype, about to be green lit.</b> For me this was the moment of epiphany, finally an answer to the question for the beginning of my career. <b>The presentation didn’t share an analysis, they hadn’t done one; it was a justification for a choice already made.</b> This was a decision based purely on hype, emotion, and identity.</bq> This is utterly unsurprising. No evaluation. Gut feeling. Justify <i>that</i> when things go tits-up. Or maybe---and stick with me here---it wouldn't have gone tits-up if you'd done an evaluation. <bq>The researchers’ conclusion was stark: <b>“To consider an alternative view, you have to imagine an alternative version of yourself.”</b> Your brain can’t objectively evaluate challenges to identity based beliefs because doing so requires temporarily dismantling the neural architecture that defines who you are. It’s not a matter of being more rational or trying harder. <b>The mechanism that would allow you to see the bias clearly is the same mechanism the bias has compromised.</b></bq> <bq>Every time an engineer evaluates a language that isn’t “theirs,” their brain is literally working against them. They’re not just analyzing technical trade offs, <b>they’re contemplating a version of themselves that doesn’t exist yet, that feels threatening to the version that does.</b> The Python developer reads case studies about Go’s performance and their amygdala quietly marks each one as a threat to be neutralized. <b>The Rust advocate looks at identical problems and their Default Mode Network constructs narratives about why “only” Rust can solve them.</b></bq> <bq>The moment you hire a Rust developer to evaluate languages, you’ve already chosen Rust. <b>You’ve just added a $2 million feasibility study to make the predetermined decision feel rational.</b></bq> <bq><b>Industry research suggests that technology stack decisions account for 40-60% of total development costs over a product’s lifecycle.</b> Research by Stripe found that developers spend 42% of their time on technical debt.</bq> <bq><b>Instead of asking “which language is best?” we need to ask “what is this language going to cost us?”</b> Not just in salaries, but in velocity, in technical debt, in hiring difficulty, in operational complexity, in every dimension that actually determines whether you survive.</bq> <bq><b>Choosing a programming language is the single most expensive economic decision your company will make.</b> It will define your culture, constrain your budget, determine your hiring pipeline, set your operational costs, and ultimately dictate whether you can move fast enough to win your market.</bq> This goes for frameworks and technologies as well.</ft> <ft>Something that I've been writing about quite extensively recently. See, e.g., <ul> <a href="{app}/view_article.php?id=5903">Discussing DI, IOC, and containers</a> <a href="{app}/view_article.php?id=5982">Thinking about the null-object pattern</a> <a href="{app}/view_article.php?id=5857">The goal is to test everything automatically</a> </ul></ft>