Yet Another Effect System (λÆS)
λÆS is an experimental effect system in Scala inspired by the ideas behind Algebraic Effects. Using Scala 3 context parameters and context functions, it provides a way to define and handle effects in a modular and composable manner.
🎥 Featured Talk
Watch the talk from Scalar 2025 about the main concepts behind the library:
📦 Available Modules
yaes-core
: The main effects of the λÆS libraryyaes-data
: Functional data structures that complement the λÆS effects system
λÆS Core
The core module provides a comprehensive set of effects for functional programming:
- IO operations and side effect management
- Structured concurrency with async computations
- Typed error handling and resource management
- Stateful computations with the State effect
- Console I/O, logging, and system integration
λÆS Data
The data module provides functional data structures optimized for use with effects:
- Flow: Cold asynchronous data streams with rich transformation operators
- Future additions: Immutable collections, persistent data structures
🚀 Quick Start
Add the dependencies to your build.sbt
:
libraryDependencies ++= Seq(
"in.rcard.yaes" %% "yaes-core" % "0.5.0",
"in.rcard.yaes" %% "yaes-data" % "0.5.0" // Optional: for Flow and other data structures
)
✨ What’s New in λÆS?
You can choose between monadic style:
import in.rcard.yaes.Random.*
import in.rcard.yaes.Raise.*
import in.rcard.yaes.Yaes.*
def drunkFlip(using Random, Raise[String]): String = for {
caught <- Random.nextBoolean
heads <- if (caught) Random.nextBoolean else Raise.raise("We dropped the coin")
} yield if (heads) "Heads" else "Tails"
Or a more direct style:
import in.rcard.yaes.Random.*
import in.rcard.yaes.Raise.*
def drunkFlip(using Random, Raise[String]): String = {
val caught = Random.nextBoolean
if (caught) {
val heads = Random.nextBoolean
if (heads) "Heads" else "Tails"
} else {
Raise.raise("We dropped the coin")
}
}
🎯 Core Concepts
In λÆS, types like Random
and Raise
are Effects:
- A Side Effect is an unpredictable interaction, usually with an external system
- An Effect System manages Side Effects by tracking and wrapping them into Effects
- An Effect describes the type of the Side Effect and the return type of an effectful computation
λÆS uses deferred execution - calling effectful functions returns a value that represents something that can be run but hasn’t yet.
🛠 Effect Management
Effects are managed using Handlers:
import in.rcard.yaes.Random.*
import in.rcard.yaes.Raise.*
val result: String = Raise.run {
Random.run {
drunkFlip
}
}
📚 Available Effects
- IO - Side-effecting operations
- Async - Asynchronous computations and fiber management
- Raise - Error handling and propagation
- State - Stateful computations and mutable state management
- Resource - Automatic resource management
- Input - Console input operations
- Output - Console output operations
- Random - Random content generation
- Clock - Time management
- System - System properties and environment variables
- Log - Logging at different levels
🗃 Data Structures
- Flow - Cold asynchronous data streams with rich operators
- More data structures - Additional functional data structures
🤝 Contributing
Contributions are welcome! Please check our contributing guidelines to get started.
🙏 Acknowledgments
Special thanks to all the smart engineers who helped with ideas and suggestions, including Daniel Ciocîrlan, Simon Vergauwen, Jon Pretty, Noel Welsh, and Flavio Brasil.