✓
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)
Supporting Files
// JavaScript implementation facet of module `fs`.
// Events and types are declared in fs.kz (merged at import); this file holds
// the |js proc bodies. What the fixture pins is the obligation lifecycle, so
// the Zig facet's page_allocator.create/destroy is modelled by a plain object
// and a no-op release.
~proc open|js {
return { handle: 42 };
}
~proc lock|js {
return file;
}
~proc write|js {
console.log(`Writing: ${data}`);
return { tag: "written" };
}
~proc unlock|js {
console.log("Unlocking");
}
~proc force-close|js {
console.log("Force closing");
}
// 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);
}
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")
Test Configuration
MUST_RUN
koru.json:
{
"paths": {
"app": ["."]
}
}