I’m excited to announce heftia-effects
, a new extensible effects library for Haskell.
Key Features
-
Correct Semantics for Higher-Order Effects & Continuations
Heftia provides the following features simultaneously, which existing libraries could not support together:
- Higher-order effects
- Delimited continuations (algebraic effects)
- Coroutines (non-scoped resumptions)
- Non-deterministic computations
MonadUnliftIO
All of these interact through a simple, consistent, and predictable semantics based on algebraic effects.
-
Easy and Concise Implementation for Custom Effect Interpreters
As you can see from the implementations of basic effect interpreters such as State, Throw/Catch, Writer, NonDet, and Coroutine, they can be implemented in just a few lines, or even a single line. Even for effects like NonDet and Coroutine, which involve continuations and might seem difficult to implement at first glance, this is exactly how simple it can be. This is the power of algebraic effects. Users can quickly define experimental and innovative custom effects using continuations.
-
Standard and Reasonable Performance
It operates at a speed positioned roughly in the middle between faster libraries (like
effectful
oreveff
) and relatively slower ones (likepolysemy
orfused-effects
): performance.md. -
Purity
- Does not depend on the IO monad and can use any monad as the base monad.
- Semantics are isolated from the IO monad, meaning that aspects like asynchronous exceptions and threads do not affect the behavior of effects.
If you’re interested, please take a look at the documentation for usage and semantics.
I’d love to hear your thoughts!
There are countless effect system libraries in Haskell, so to be honest, I was unsure whether to announce this library. However, after several updates, I believe that Heftia v0.4 now offers value comparable to other major effect system libraries. The current version is no longer experimental and aims to provide a practical effect system.
More Details
Background
Traditional libraries have always struggled with semantic issues in higher-order effects*1*2*3*4.
Heftia is based on algebraic effects and provides a simple, consistent, and predictable semantics for all interactions of effects, thereby providing answers to these issues comprehensively.
Effect and its interpreter designers only need to consider the semantics of algebraic effects and do not need to worry about the library’s internal implementation or IO monad-level details*5.
This significantly simplifies the process of creating custom effects and writing their interpreters.
Type safety ensures that there is no concern about breaking semantics, allowing users to focus solely on the inherent complexity of their tasks.
Robustness of Semantics
This library prevents inherently dangerous operations*6 that could break or obscure semantics through type safety.
This is not something added in an ad-hoc manner through methods like smart constructors but is naturally derived from the straightforward data structure of the Eff
monad.
The constructors of the Eff
monad are exposed, and users can manipulate them directly without any safety concerns, yet the semantics remain intact.
Performance
This library is based on the Freer monad, which is somewhat traditional, rather than using the recent mainstream approaches like evidence passing (ReaderT IO pattern) used by effectful
, cleff
, or bluefin
*7. It’s not extremely fast, but it’s built on the fast implementation freer-simple
, resulting in a speed that is positioned roughly in the middle between the high-speed IO-based implementations and relatively slower implementations. In typical usage, you should find the performance satisfactory.
Additionally, by basing on Freer, the semantics of the effect system are separated from the semantics of IO. This means that when interpreting effects, there is no need to consider IO-level details*5. The consistent semantics of algebraic effects prevent leaky abstraction from the IO level. This is a significant difference from IO
-fused effect system libraries like effectful and cleff, and it’s one of the reasons why this library is based on Freer rather than on IO.
*1: The effect semantics zoo - lexi-lambda/eff
*2: Incorrect semantics for higher-order effects - hasura/eff#12
*3: Semantics of higher order effects - re-xyr/speff#3
*4: Unresolved challenges of scoped effects
*5: Examples include “Asynchronous exceptions might be thrown,” “What is the current state of exception masking,” “This state/environment value is local and thus not shared between threads,” and more.
*6: For instance, interpreting the coroutine effect while the catch effect is still uninterpreted and interpreting the catch effect afterwards.
*7: In particular, Heftia doesn’t use GHC’s delimited continuation primops.