○
Planned This feature is planned but not yet implemented.
Optional branches feature not fully implemented - see 918_optional_branches/IMPLEMENTATION_ROADMAP.md
Code
// Test 918f: Optional Branches - API Evolution (Anti-F# Discard)
//
// This test demonstrates:
// 1. Event evolves by adding NEW OPTIONAL branch
// 2. Old handlers (written before new branch) still work
// 3. |? catch-all catches the new optional branch
// 4. This is CORRECT: optional branches are supplementary
//
// Contrast with F# discard which would catch REQUIRED branches too (bad!)
const std = @import("std");
// ===== VERSION 2 OF API =====
// This event has been evolved from Version 1 by adding ?debug branch
~event process { value: u32 }
| success { result: u32 } // REQUIRED (in both versions)
| ?warning { msg: []const u8 } // OPTIONAL (in both versions)
| ?debug { info: []const u8 } // OPTIONAL (NEW in Version 2)
~proc process {
const doubled = value * 2;
// Version 2 proc now returns the new debug branch in some cases
if (value < 5) {
return .{ .debug = .{ .info = "Small value" } };
}
if (value > 100) {
return .{ .warning = .{ .msg = "Large value" } };
}
std.debug.print("Success: {}\n", .{doubled});
return .{ .success = .{ .result = doubled } };
}
// ===== HANDLER WRITTEN FOR VERSION 1 =====
// This handler was written when ?debug didn't exist
// It only knows about: success (required), warning (optional)
// The |? catch-all SILENTLY catches the new ?debug branch
// This is CORRECT because ?debug is OPTIONAL (supplementary info)
~process(value: 10)
| success { result } |> std.debug.print("Handler: success {}\n", .{result})
|? |> std.debug.print("Handler: optional branch\n", .{})
~process(value: 150)
| success { result } |> std.debug.print("Handler: success {}\n", .{result})
|? |> std.debug.print("Handler: optional branch\n", .{})
~process(value: 2)
| success { result } |> std.debug.print("Handler: success {}\n", .{result})
|? |> std.debug.print("Handler: optional branch\n", .{})
// ===== WHAT IF WE ADDED A REQUIRED BRANCH INSTEAD? =====
// If we changed ?debug to just "debug" (required), then:
// - These handlers would FAIL TO COMPILE
// - Shape checker would error: "handler missing required 'debug' branch"
// - Cascade compilation errors force you to update ALL handlers
// - This is HOW you evolve APIs correctly in Koru
//
// This is the key difference from F#'s _ discard:
// - Koru |? only catches OPTIONAL branches
// - Adding REQUIRED branches → compile errors everywhere (good!)
// - Adding OPTIONAL branches → silently caught (also good!)
Expected Output
Success: 20
Handler: success 20
Handler: optional branch
Handler: optional branch
Test Configuration
MUST_RUN