Core Concepts
Core Concepts
Section titled “Core Concepts”This page explains the ideas that everything in λÆS is built on. Understanding these concepts will make the rest of the documentation click into place.
What is an Effect?
Section titled “What is an Effect?”In λÆS, an Effect is a managed side effect. To understand what that means, start from the more familiar idea:
- A Side Effect is an unpredictable interaction, usually with an external system — reading from a file, generating a random number, or throwing an exception.
- An Effect System manages side effects by tracking and wrapping them. Instead of letting side effects happen implicitly, the system makes them explicit in function signatures.
- An Effect describes what kind of side effect a function needs, and what type of value it produces.
In λÆS, types like Random, Raise[E], Async, and State[S] are effect types. When a function declares using Random, it is saying: “I need access to randomness, and that requirement must be satisfied by a caller.”
import in.rcard.yaes.Random.*
// This function requires the Random effectdef rollDie(using Random): Int = Random.nextInt(6) + 1The key insight: effects appear in the type signature. There are no hidden surprises — you can read a function’s requirements directly from its parameters.
Side Effects vs Effects
Section titled “Side Effects vs Effects”| Side Effect (bad) | Effect (good) |
|---|---|
| Happens implicitly | Declared in function signature |
| Cannot be composed | Composes with other effects |
| Hard to test | Can be swapped for a test handler |
| Untracked | Tracked by the type system |
λÆS does not eliminate side effects — it manages them. Under the hood, a handler runs the real operation. But the function itself is pure from the caller’s perspective.
Deferred Execution
Section titled “Deferred Execution”Calling an effectful function does not immediately execute it. Instead, it returns a value representing a computation that can be run later. Execution only happens when a handler is applied.
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") }}
// Nothing runs until here:val result: Either[String, String] = Raise.run { Random.run { drunkFlip }}Deferred execution means:
- Composability: Multiple effects can be combined before any runs.
- Testability: You can substitute a test handler without changing the function.
- Safety: The type system ensures every effect is handled before the program runs.
Handlers
Section titled “Handlers”A handler is the mechanism that executes an effect. Every effect type in λÆS has a corresponding handler. Handlers:
- Provide the capability the effect requires (e.g., a random number generator)
- Interpret the effect’s operations (e.g., translate
Raise.raiseintoLeft(...)) - Return a result
Handlers are applied by calling EffectType.run { ... }:
import in.rcard.yaes.Output.*
val result: Unit = Output.run { Output.printLn("Hello, λÆS!")}Handlers can be composed — you wrap one inside another. The innermost handler runs first:
import in.rcard.yaes.Random.*import in.rcard.yaes.Output.*
val result: Unit = Output.run { Random.run { val n = Random.nextInt(10) Output.printLn(s"Random number: $n") }}The order of handler application can matter for effects that interact (e.g., Raise inside Async).
Effect Composition
Section titled “Effect Composition”Effects are declared as multiple using parameters and composed naturally:
import in.rcard.yaes.Random.*import in.rcard.yaes.Output.*import in.rcard.yaes.Raise.*
def gameRound(using Random, Output, Raise[String]): Int = { val dice1 = Random.nextInt(6) + 1 val dice2 = Random.nextInt(6) + 1 val total = dice1 + dice2
Output.printLn(s"Rolled: $dice1 + $dice2 = $total")
if (total == 7) Raise.raise("Lucky seven!") total}You can handle effects one at a time or all at once. Effects that are not yet handled are propagated to callers — the type system tracks them.
Available Effects
Section titled “Available Effects”λÆS provides a comprehensive set of effects:
Core Effects
Sync— wraps arbitrary side-effecting codeAsync— structured concurrency and fibersRaise[E]— typed error handlingState[S]— stateful computations
Resource Management
Resource— acquire/release with guaranteed cleanupShutdown— graceful termination with hooks
I/O and Utilities
Input/Output— console I/ORandom— random number generationSystemClock/SystemEnv/SystemProperties— clock and environment accessLog— structured logging (via SLF4J backend)
Retry
Retry— retry failing blocks with configurable schedules
Continue to Basic Effects to see these in action.