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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user