Why Effects? >

Test Hex.pm Documentation

An effectful programming framework for Elixir.

                          Comp
                     (lazy computation,
                      evidence-passing,
                      scoped handlers)
                            
      
                                                      
 //Foundational       //Coroutines &               //Boundaries
 //Effects            //Concurrency                     
                                                      
                       Coroutine                
                                                         
 State, Reader,                      Port
 Writer, Throw,                                       Port.EffectfulFacade
 Bracket, Fresh,     Serializable-                     Repo
 Random, FxList,      Coroutine                      
 Yield,                                              
 EffectLogger,  AsyncCoroutine     FiberPool       Adapter
 Parallel,                                        Adapter.EffectfulContract
 AtomicState,                         
 Transaction,                                  
 Command                                       
                                     
                              Channel    Task   
                                               
                               Brook            
                                                
                                           Query.Contract
                                           QueryBlock
                                           (Haxl-like: auto-batches fetches
                                           via Coroutine fibers)

The old problem

Between pure business logic and side-effecting infrastructure sits the orchestration layer — "fetch the user, check permissions, load their subscription, hit some APIs, compute a price, write an invoice." This code encodes your most important business rules, but it's tangled with databases, APIs, and randomness — making it hard to test, hard to refactor, and often — impossible to property-test.

Another way

Skuld lets you write orchestration code that describes side effects without performing them — then handlers decide what those descriptions mean. The exact same "effectful" code runs with side-effecting handlers in production and pure in-memory handlers in tests — fully deterministic, fully pure, and straightforwardly property-testable.

Because effects are first-class data, Skuld can do more — batch independent queries automatically, serialise partially complete computations for later resumption.

Quick example

defmodule Onboarding do
  use Skuld.Syntax

  alias Skuld.Repo
  alias Skuld.Effects.{Fresh, Reader, Writer}

  defcomp register(params) do
    config <- Reader.ask()
    id <- Fresh.fresh_uuid()
    {:ok, user} <- Repo.insert(User.changeset(%{id: id, name: params.name, tier: config.default_tier}))
    _ <- Writer.tell(:events, %UserRegistered{user_id: id})
    {:ok, user}
  end
end

Run with production handlers:

# One-time setup: generate an Ecto adapter for the Repo contract
defmodule MyApp.Repo.Port do
  use Skuld.Repo.Ecto, repo: MyApp.Repo
end

# Wire everything up:
Onboarding.register(%{name: "Alice"})
|> Reader.with_handler(%{default_tier: :free})
|> Fresh.with_uuid7_handler()
|> Port.with_handler(%{Skuld.Repo.Effectful => MyApp.Repo.Port})
|> Writer.with_handler([], tag: :events, output: fn r, raw ->
  MyApp.EventBus.publish(Enum.reverse(raw))
  r
end)
|> Throw.with_handler()
|> Comp.run!()

Run with test handlers — same code, fully deterministic, no database:

Onboarding.register(%{name: "Alice"})
|> Reader.with_handler(%{default_tier: :free})
|> Fresh.with_test_handler()
|> Repo.InMemory.with_handler(Repo.InMemory.new())
|> Writer.with_handler([], tag: :events, output: fn r, raw -> {r, Enum.reverse(raw)} end)
|> Throw.with_handler()
|> Comp.run!()

Repo.InMemory is a closed-world in-memory store with read-after-write consistency. Records created during the test are immediately readable by subsequent Repo.get / Repo.get_by calls — no mocks, no stubs.

Installation

def deps do
  [
    {:skuld, "~> 0.27"}
  ]
end

Where next?

If you want to...Read
Understand the problem effects solveWhy Effects?
See how effects and handlers workHow It Works
Write your first computationGetting Started
State, Reader, Writer, Throw, Fresh, RandomFoundational Effects
Yield, Coroutines, FiberPool, Channels, AsyncCoroutines & Concurrency
Port, Repo, Hexagonal ArchitectureBoundaries
Eliminate N+1 queriesQuery System
Handler-swapping for deterministic testingTesting
Full effect and API referenceReference

License

MIT License — see LICENSE for details.


Why Effects? >