feat: implement evidence passing for O(1) effect handler lookup

Interpreter changes:
- Add evidence HashMap for O(1) handler lookup instead of O(n) stack search
- Update eval_run to manage evidence when entering/exiting run blocks
- Modify handle_effect to use evidence.get() instead of stack iteration

C backend infrastructure:
- Add handler structs (LuxConsoleHandler, LuxStateHandler, LuxReaderHandler)
- Add LuxEvidence struct containing pointers to all handlers
- Add default handlers that delegate to built-in implementations
- Add Console.readLine built-in implementation

Documentation:
- Create docs/EVIDENCE_PASSING.md explaining design and implementation
- Update docs/C_BACKEND.md with current progress

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 11:52:00 -05:00
parent 909dbf7a97
commit ce4ab45651
4 changed files with 336 additions and 28 deletions

View File

@@ -379,12 +379,78 @@ impl CBackend {
self.writeln(" return strstr(haystack, needle) != NULL;");
self.writeln("}");
self.writeln("");
self.writeln("// === Console Effect (built-in) ===");
self.writeln("// === Built-in Effect Implementations ===");
self.writeln("");
self.writeln("static void lux_console_print(LuxString msg) {");
self.writeln(" printf(\"%s\\n\", msg);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_console_readLine(void) {");
self.writeln(" char buffer[4096];");
self.writeln(" if (fgets(buffer, sizeof(buffer), stdin)) {");
self.writeln(" size_t len = strlen(buffer);");
self.writeln(" if (len > 0 && buffer[len-1] == '\\n') buffer[len-1] = '\\0';");
self.writeln(" return strdup(buffer);");
self.writeln(" }");
self.writeln(" return strdup(\"\");");
self.writeln("}");
self.writeln("");
self.writeln("// === Evidence Passing Types ===");
self.writeln("// These enable O(1) effect handler lookup instead of O(n) stack search.");
self.writeln("// See docs/EVIDENCE_PASSING.md for details.");
self.writeln("");
self.writeln("// Handler struct for Console effect");
self.writeln("typedef struct {");
self.writeln(" void (*print)(void* env, LuxString msg);");
self.writeln(" LuxString (*readLine)(void* env);");
self.writeln(" void* env;");
self.writeln("} LuxConsoleHandler;");
self.writeln("");
self.writeln("// Handler struct for State effect");
self.writeln("typedef struct {");
self.writeln(" void* (*get)(void* env);");
self.writeln(" void (*put)(void* env, void* value);");
self.writeln(" void* env;");
self.writeln("} LuxStateHandler;");
self.writeln("");
self.writeln("// Handler struct for Reader effect");
self.writeln("typedef struct {");
self.writeln(" void* (*ask)(void* env);");
self.writeln(" void* env;");
self.writeln("} LuxReaderHandler;");
self.writeln("");
self.writeln("// Evidence struct - passed to effectful functions");
self.writeln("// Contains pointers to current handlers for each effect type");
self.writeln("typedef struct {");
self.writeln(" LuxConsoleHandler* console;");
self.writeln(" LuxStateHandler* state;");
self.writeln(" LuxReaderHandler* reader;");
self.writeln("} LuxEvidence;");
self.writeln("");
self.writeln("// Default Console handler using built-in implementations");
self.writeln("static void default_console_print(void* env, LuxString msg) {");
self.writeln(" (void)env;");
self.writeln(" lux_console_print(msg);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString default_console_readLine(void* env) {");
self.writeln(" (void)env;");
self.writeln(" return lux_console_readLine();");
self.writeln("}");
self.writeln("");
self.writeln("static LuxConsoleHandler default_console_handler = {");
self.writeln(" .print = default_console_print,");
self.writeln(" .readLine = default_console_readLine,");
self.writeln(" .env = NULL");
self.writeln("};");
self.writeln("");
self.writeln("// Default evidence with built-in handlers");
self.writeln("static LuxEvidence default_evidence = {");
self.writeln(" .console = &default_console_handler,");
self.writeln(" .state = NULL,");
self.writeln(" .reader = NULL");
self.writeln("};");
self.writeln("");
self.writeln("// === List Types ===");
self.writeln("");
self.writeln("typedef struct {");

View File

@@ -557,8 +557,11 @@ impl std::fmt::Debug for StoredMigration {
pub struct Interpreter {
global_env: Env,
/// Stack of active effect handlers
/// Stack of active effect handlers (kept for nested handler semantics)
handler_stack: Vec<Rc<HandlerValue>>,
/// Evidence map for O(1) handler lookup (evidence passing optimization)
/// Maps effect name -> handler, updated when entering/exiting run blocks
evidence: HashMap<String, Rc<HandlerValue>>,
/// Stored continuations for resumption
continuations: HashMap<usize, Box<dyn FnOnce(Value) -> Result<EvalResult, RuntimeError>>>,
/// Effect tracing for debugging
@@ -609,6 +612,7 @@ impl Interpreter {
Self {
global_env,
handler_stack: Vec::new(),
evidence: HashMap::new(),
continuations: HashMap::new(),
trace_effects: false,
effect_traces: Vec::new(),
@@ -2871,15 +2875,36 @@ impl Interpreter {
}
}
// Push handlers
// Push handlers onto stack (for nested handler semantics)
for h in &handler_values {
self.handler_stack.push(Rc::clone(h));
}
// Update evidence map for O(1) lookup (evidence passing optimization)
// Save previous evidence values for restoration
let mut previous_evidence: Vec<(String, Option<Rc<HandlerValue>>)> = Vec::new();
for h in &handler_values {
let effect_name = h.effect.clone();
let prev = self.evidence.insert(effect_name.clone(), Rc::clone(h));
previous_evidence.push((effect_name, prev));
}
// Evaluate expression
let result = self.eval_expr_inner(expr, env);
// Pop handlers
// Restore previous evidence (for proper nested handler support)
for (effect_name, prev) in previous_evidence.into_iter().rev() {
match prev {
Some(h) => {
self.evidence.insert(effect_name, h);
}
None => {
self.evidence.remove(&effect_name);
}
}
}
// Pop handlers from stack
for _ in &handler_values {
self.handler_stack.pop();
}
@@ -2895,12 +2920,11 @@ impl Interpreter {
0
};
// Find a handler for this effect - clone what we need to avoid borrow issues
// Find a handler using evidence map (O(1) lookup via evidence passing)
// This replaces the previous O(n) handler_stack search
let handler_data: Option<(Env, crate::ast::Expr, Vec<Ident>)> = self
.handler_stack
.iter()
.rev()
.find(|h| h.effect == request.effect)
.evidence
.get(&request.effect)
.and_then(|handler| {
handler
.implementations