✓
Passing This code compiles and runs correctly.
Code
// auto-discharge with MULTIPLE disposers for the same state: `locked!` can be
// discharged by EITHER `unlock` (happy path) or `force-close` (error path).
// `write` is fallible (`| written` / `| err`); its err arm force-closes the
// lock, its success arm unlocks. Both arms discharge `locked!` — the explicit
// branch picks which disposer runs.
import app/fs
app/fs:open(path: "test.txt"): f |> app/fs:lock(file: f): l |> app/fs:write(file: l, data: "test")
| written |> app/fs:unlock(file: l)
| err _ |> app/fs:force-close(file: l)
Actual
Writing: test
Unlocking
Expected output
Writing: test
Unlocking
Flows
flow ~open click a branch to expand · @labels scroll to their anchor
open (path: "test.txt")
Imported Files
// Library module: fs
// State transitions with multiple disposal options (mimics string library)
const std = @import("std");
const File = struct { handle: i32 };
// Open a file - returns opened! state
~pub tor open { path: string } -> *File<opened!>
~proc open|zig {
const f = std.heap.page_allocator.create(File) catch unreachable;
f.* = File{ .handle = 42 };
return f;
}
// Lock - transition opened! -> locked!
~pub tor lock { file: *File<!opened> } -> *File<locked!>
~proc lock|zig {
return file;
}
// Write - requires locked state (doesn't consume)
~pub tor write { file: *File<locked>, data: string }
| written
| err string
~proc write|zig {
std.debug.print("Writing: {s}\n", .{data});
return .{ .written = .{} };
}
// Unlock - consumes locked! (Option 1 for disposing locked!)
~pub tor unlock { file: *File<!locked> }
~proc unlock|zig {
std.debug.print("Unlocking\n", .{});
std.heap.page_allocator.destroy(file);
}
// Force close - also consumes locked! (Option 2 for disposing locked!)
~pub tor force-close { file: *File<!locked> }
~proc force-close|zig {
std.debug.print("Force closing\n", .{});
std.heap.page_allocator.destroy(file);
}
Test Configuration
MUST_RUN