Tidepool
Compile Haskell effect stacks into Cranelift-backed state machines drivable from Rust.
What is Tidepool?
Tidepool compiles freer-simple effect stacks from Haskell into native code via Cranelift JIT, producing effect machines that can be driven step-by-step from Rust. Write your business logic as a pure Haskell effect program, compile it once, then run it with Rust-side effect handlers that provide IO, state, networking, or anything else.
Haskell expands (describes what to do). Rust collapses (does it). The language boundary is the hylo boundary.
Getting Started
1. Install the MCP server
2. Install the GHC toolchain (requires Nix)
The Haskell compiler (tidepool-extract) is needed to evaluate code. Install it via Nix:
# Install Nix (if needed):
|
# Optional: use binary cache (skip 30min GHC build)
# Install the tidepool GHC toolchain:
Note: If
tidepool-extractis not found, the server starts in setup mode and exposes only aninstall_instructionstool that tells the calling LLM what to install.
3. Configure your MCP client
The tidepool binary is an MCP server that communicates over stdio.
Claude Code (~/.claude/settings.json or project .claude/settings.json):
You can also use mcp-wrapper.py (from the repo) to add a __mcp_restart tool for hot-restarting the server:
Environment variables:
TIDEPOOL_EXTRACT— path to thetidepool-extractbinary (falls back totidepool-extracton$PATH)TIDEPOOL_PRELUDE_DIR— override the Haskell stdlib location (normally embedded in the binary)TIDEPOOL_GHC_LIBDIR— override GHC's lib directory (avoids callingghc --print-libdir)RUST_LOG— set todebugorinfofor server diagnostics on stderr
Verify
Once configured, your MCP client should see the eval tool. Try evaluating:
pure (1 + 2 :: Int)
-- → 3
Development (from source)
Architecture
tidepool/ Facade crate + MCP server binary
tidepool-repr/ Core IR: CoreExpr, DataConTable, CBOR serialization
tidepool-eval/ Tree-walking interpreter: Value, Env, lazy evaluation
tidepool-heap/ Manual heap + copying GC for JIT runtime
tidepool-optimize/ Optimization passes: beta reduction, DCE, inlining, case reduction
tidepool-bridge/ FromCore/ToCore traits for Rust <-> Core value conversion
tidepool-bridge-derive/ Proc-macro: #[derive(FromCore)]
tidepool-macro/ Proc-macro: haskell_inline! { ... }
tidepool-effect/ Effect handling: EffectHandler trait, HList dispatch
tidepool-codegen/ Cranelift JIT compiler + effect machine
tidepool-runtime/ High-level API: compile_haskell, compile_and_run, caching
tidepool-mcp/ MCP server library (generic over effect handlers)
How It Works
- Write Haskell using
freer-simpleeffects (e.g.emit "hello" >> awaitInt) - Extract GHC Core via
tidepool-extract, which serializes to CBOR - Load in Rust as
CoreExpr+DataConTable(the IR) - Optimize with configurable passes (beta reduction, inlining, dead code elimination)
- Compile to native via Cranelift, producing a
JitEffectMachine - Run with handlers — the machine yields effect requests; Rust handlers respond
Examples
| Example | What it shows |
|---|---|
examples/guess/ |
Number guessing game. Compile-time haskell_inline!, JIT, two effects (Console + Rng). The minimal "hello world". |
examples/tide/ |
Interactive REPL with 5 effects (Repl, Console, Env, Net, Fs). Multi-effect composition at scale. |
Using as a Rust Library
Defining effects
The core pattern is three steps:
1. Haskell GADT defines the effect:
data Console a where
Emit :: String -> Console ()
AwaitInt :: Console Int
2. #[derive(FromCore)] Rust enum mirrors it:
3. impl EffectHandler provides the implementation:
Compile-time path (haskell_inline!)
use haskell_inline;
use JitEffectMachine;
let = haskell_inline! ;
let mut vm = compile?;
vm.run?;
Runtime path (compile_and_run)
let result = compile_and_run?;
println!;
Key crates
| Crate | Entry points |
|---|---|
tidepool-macro |
haskell_inline!, haskell_eval!, haskell_expr! — compile-time Haskell embedding |
tidepool-effect |
EffectHandler, EffectContext, DispatchEffect — effect dispatch traits |
tidepool-bridge-derive |
#[derive(FromCore)], #[derive(ToCore)] — Haskell↔Rust value conversion |
tidepool-runtime |
compile_and_run, compile_haskell, EvalResult — high-level runtime API |
tidepool-codegen |
JitEffectMachine — Cranelift JIT compiler + effect machine |
tidepool-mcp |
TidepoolMcpServer, DescribeEffect, EffectDecl — MCP server library |
MCP Server Effects
The tidepool binary provides these effect handlers:
| Effect | Operations |
|---|---|
| Console | Print :: Text -> Console () |
| KV | KvGet, KvSet, KvDelete, KvKeys — persistent key-value store |
| Fs | FsRead, FsWrite, FsListDir, FsGlob, FsExists, FsMetadata — sandboxed file I/O |
| SG | SgFind, SgPreview, SgReplace, SgRuleFind, SgRuleReplace — structural code search via ast-grep |
| Http | HttpGet, HttpPost, HttpRequest — outbound HTTP (no localhost) |
| Exec | Run, RunIn, RunJson — shell command execution |
| Meta | MetaConstructors, MetaLookupCon, MetaPrimOps, MetaEffects, MetaDiagnostics, MetaVersion, MetaHelp |
| Git | GitLog, GitShow, GitDiff, GitBlame, GitTree, GitBranches — native git access via libgit2 |
| Ask | Ask :: Text -> Ask Value — suspend execution and ask the calling LLM a question |
Development
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.