flow-effects
Skill1 starsChanged 3 months agoMITView on GitHub
Use Flow's algebraic effects to separate what code needs from who provides it. Use for logging, clocks, I/O, randomness, dependency injection, mocking, test seams, or any request to swap an implementation without changing call sites.
In this repository13 other files it gives its agents.
- AGENTS.mdAGENTS.md
- flow-audioSkill
- flow-autodiffSkill
- flow-basicsSkill
- flow-compiler-devSkill
- flow-debugSkill
- flow-dynamicsSkill
- flow-graphicsSkill
- flow-perfSkill
- flow-setupSkill
- flow-stdlibSkill
- flow-verifySkill
- flow-wasmSkill
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 2 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 122
Use strict mode whenever you are verifying that handlers are actually reached.
- Very specific: line 131
Keep capabilities as stateless policy.
- Distinctive: line 158
Run the same logic under two handler stacks and assert the results differ in the way the design predicts.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Algebraic effects
An effect declares operations. Business logic calls them. A handler supplies them for a dynamic scope. Call sites never change.
Requires FLOW_HOST=python.
Declare, call, handle
effect Log {
info(msg: string) -> void,
warn(msg: string) -> void,
}
effect Clock {
now() -> i32,
}
effect Inventory {
stock_of(sku: i32) -> i32,
reserve(sku: i32, qty: i32) -> i32,
}Business logic depends only on the effect. No handler is passed in.
function place_order(sku: i32, qty: i32) -> i32 {
Log.info("order received")
let available: i32 = Inventory.stock_of(sku)
if available < qty {
Log.warn("insufficient stock")
return -1
}
return Inventory.reserve(sku, qty)
}A handler is a capability. It names the effect or effects it implements and
gives a function per operation. Note the commas after each member.
capability WallClock {
effect Clock,
function now() -> i32 {
return 1721224800
},
}
capability OpsLogger {
effect Log,
function info(msg: string) -> void {
let t: i32 = Clock.now() # a handler may call other effects
printf(" [%d] INFO %s\n", t, msg)
},
function warn(msg: string) -> void {
printf(" WARN %s\n", msg)
},
}
capability TestBackend {
effect Inventory, Notify, # one capability, several effects
function stock_of(sku: i32) -> i32 {
return 99
},
function reserve(sku: i32, qty: i32) -> i32 {
return 1
},
function send(recipient: string, msg: string) -> void {
},
}A handler is installed for a scope:
function main() -> i32 {
let mut accepted: i32 = 0
handle Clock with WallClock {
handle Log with OpsLogger {
handle Inventory with WarehouseInventory {
accepted = place_order(1001, 2)
}
}
}
handle Inventory with TestBackend {
accepted = place_order(1001, 2)
}
return 0
}The same place_order runs against production and test handlers. Nested
handles shadow outer ones and the outer handler is restored on exit.
The trap
With no handler installed, an effect operation falls back to a zeroed default and returns quietly. A program can look healthy while doing nothing.
Two defences:
FLOW_STRICT_EFFECTS=1 FLOW_HOST=python flow run program.flowand, at compile time:
FLOW_STRICT_EFFECTS=1 FLOW_HOST=python flow compile program.flow --strict-effectsUse strict mode whenever you are verifying that handlers are actually reached.
Do not pass handlers as parameters
Dispatch goes through the effect's current handler pointer. Passing a stateful struct into a function does nothing; the call still dispatches through the installed handler or the default. Several older examples carry a header explaining that they were rewritten for exactly this reason.
Keep capabilities as stateless policy. Put mutable state in ordinary let mut
locals in the caller and thread it through the handle scope.
Read these first
cat examples/effects/showcase.flow # one service, many worlds
cat examples/effects/dependency_injection.flow
cat examples/effects/async_primitives.flow # stdlib Async and AsyncIO
cat examples/effects/state_effects.flow
cat docs/effects-showcase.md # includes the honest limitationslib/stdlib/async.flow provides Async and AsyncIO. There is no fiber
scheduler; SimulatedAsync is a deterministic synchronous stand-in and
BlockingAsyncIO.sleep_ms blocks. Say this rather than implying concurrency.
When effects earn their place
Good fits: logging, clocks, randomness, storage, network, telemetry, anything you want to fake in a test.
Poor fits: pure arithmetic, tight numerical inner loops, anything where the indirection buys nothing.
Verify
Run the same logic under two handler stacks and assert the results differ in the way the design predicts. That is the property effects give you, so it is the property to test.
if production_result == test_result {
println("FAIL: handlers were not distinguished")
return 1
}Report the command with FLOW_STRICT_EFFECTS=1 included, so the reader knows
unhandled operations would have aborted.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

