vihaco / virtual ISA & machine framework
Guides
  1. Defining Instructions
  2. Advanced Instruction Usage
  3. Parser Integration
  4. Pattern Parser Generator
  5. Advanced Parser Customization
  6. Using Messages
  7. Building Components
  8. Observing Effects
  9. Defining a Composite

Observing Effects With Observe

Observers inspect an effect without consuming it. Implement Observe<E, R> for a field that records traces, updates metrics, or performs diagnostics. R is the composite route marker, so the same effect type can be observed differently on different routes.

use eyre::Result;
use vihaco::{Effects, Observe};

#[derive(Debug)]
struct Line(String);

#[derive(Default)]
struct Logger { lines: Vec<String> }

impl<R> Observe<Line, R> for Logger {
    type Effect = ();
    type Error = eyre::Report;

    fn observe(&mut self, line: &Line) -> Result<Effects<()>> {
        self.lines.push(line.0.clone());
        Ok(Effects::none())
    }
}

In a composite! route, list observers in the order they should run:

effects {
    observe logger, metrics;
    absorb with output_stack;
}

Each observer borrows the same effect. The handler then receives ownership exactly once. This makes the ownership flow clear:

Execute -> Effects<E> -> Observe(&E) ... -> Handle(E)

The observer’s associated Effect is reserved for typed follow-up work. The current generated route dispatch does not automatically schedule those follow-up effects; a future runtime extension may make that continuation explicit. Until then, return Effects::none() or handle follow-up effects in your own runtime boundary.

An observer is an ordinary component field; it does not need an instruction catalog or a message source. A component can also implement Observe when it needs to react to another component’s output.

See Defining a Composite for effect handlers and route selection.