✓
Passing This code compiles and runs correctly.
Code
// TEST: Database transaction pattern with layered obligations
// STATUS: IMPLEMENTED
//
// Real-world pattern: connect → query → (commit OR rollback) → disconnect
//
// Two obligations that must be satisfied:
// 1. connected! on Connection - must call disconnect
// 2. transaction! on Transaction - must call commit OR rollback
//
// This tests that:
// - Multiple obligations can coexist
// - Each branch (then/else) must handle the transaction obligation
// - Connection obligation auto-discharged at terminal in both branches
~import "$std/control"
~import "$app/db"
const query_succeeded = true;
~app.db:connect(url: "postgres://localhost/test")
| connected c |>
app.db:query(conn: c.conn, sql: "SELECT * FROM users")
| started tx |>
std.control:if(query_succeeded)
| then |>
app.db:commit(tx: tx.tx)
| committed |>
app.db:disconnect(conn: c.conn)
| disconnected |> _
| else |>
app.db:rollback(tx: tx.tx)
| rolledback |>
app.db:disconnect(conn: c.conn)
| disconnected |> _
pub fn main() void {}
Imported Files
// Library module: db
// Database operations with layered cleanup obligations
//
// Pattern: connect → query → (commit OR rollback) → disconnect
// Two obligations:
// 1. connected! on Connection - must call disconnect
// 2. transaction! on Transaction - must call commit or rollback
const std = @import("std");
const Connection = struct {
id: i32,
};
const Transaction = struct {
conn: *Connection,
};
// Connect to database - creates connected! obligation
~pub event connect { url: []const u8 }
| connected { conn: *Connection[connected!] }
~proc connect {
std.debug.print("Connecting to: {s}\n", .{url});
const allocator = std.heap.page_allocator;
const c = allocator.create(Connection) catch unreachable;
c.* = Connection{ .id = 1 };
return .{ .connected = .{ .conn = c } };
}
// Start a query/transaction - creates transaction! obligation
~pub event query { conn: *Connection[connected!], sql: []const u8 }
| started { tx: *Transaction[transaction!] }
~proc query {
std.debug.print("Query: {s}\n", .{sql});
const allocator = std.heap.page_allocator;
const t = allocator.create(Transaction) catch unreachable;
t.* = Transaction{ .conn = conn };
return .{ .started = .{ .tx = t } };
}
// Commit - CONSUMES transaction! obligation
~pub event commit { tx: *Transaction[!transaction] }
| committed {}
~proc commit {
std.debug.print("Committing transaction\n", .{});
return .{ .committed = .{} };
}
// Rollback - CONSUMES transaction! obligation (alternative to commit)
~pub event rollback { tx: *Transaction[!transaction] }
| rolledback {}
~proc rollback {
std.debug.print("Rolling back transaction\n", .{});
return .{ .rolledback = .{} };
}
// Disconnect - CONSUMES connected! obligation
~pub event disconnect { conn: *Connection[!connected] }
| disconnected {}
~proc disconnect {
std.debug.print("Disconnecting\n", .{});
return .{ .disconnected = .{} };
}