Skip to main content

oxilean_std/algebraic_effects/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::HashMap;
6
7use super::functions::*;
8use std::fmt;
9
10/// An algebraic effect given by its signature (operation, parameter type, return type).
11pub struct AlgebraicEffect {
12    /// Triples (operation name, parameter type, return type).
13    pub signature: Vec<(String, String, String)>,
14}
15impl AlgebraicEffect {
16    /// Create a new AlgebraicEffect.
17    pub fn new(signature: Vec<(String, String, String)>) -> Self {
18        Self { signature }
19    }
20    /// The equational theory of the algebraic effect.
21    pub fn effect_theory(&self) -> String {
22        format!(
23            "Algebraic theory with {} operations; models are algebras satisfying handler equations",
24            self.signature.len()
25        )
26    }
27    /// Operations are given by equations between terms containing effect calls.
28    pub fn operations_are_equations(&self) -> bool {
29        true
30    }
31}
32/// A type-erased representation of the Freer monad for string-typed operations.
33///
34/// `FreerComp` is either a pure value or an impure node holding an operation
35/// description and a continuation.
36#[allow(dead_code)]
37pub enum FreerComp {
38    /// Pure computation: just a string value.
39    Pure(String),
40    /// Impure: an effect name, operation name, argument, and a continuation.
41    Impure {
42        /// Effect name.
43        effect: String,
44        /// Operation name.
45        op: String,
46        /// Argument (serialized).
47        arg: String,
48        /// Continuation from result string to next computation.
49        cont: Box<dyn FnOnce(String) -> FreerComp>,
50    },
51}
52#[allow(dead_code)]
53impl FreerComp {
54    /// Construct a pure Freer computation.
55    pub fn pure(v: impl Into<String>) -> Self {
56        FreerComp::Pure(v.into())
57    }
58    /// Construct an impure Freer computation (one algebraic operation).
59    pub fn impure(
60        effect: impl Into<String>,
61        op: impl Into<String>,
62        arg: impl Into<String>,
63        cont: impl FnOnce(String) -> FreerComp + 'static,
64    ) -> Self {
65        FreerComp::Impure {
66            effect: effect.into(),
67            op: op.into(),
68            arg: arg.into(),
69            cont: Box::new(cont),
70        }
71    }
72    /// Interpret the Freer computation using a handler function.
73    /// `handler(effect, op, arg)` returns the result string for that operation.
74    pub fn interpret(self, handler: &dyn Fn(&str, &str, &str) -> String) -> String {
75        match self {
76            FreerComp::Pure(v) => v,
77            FreerComp::Impure {
78                effect,
79                op,
80                arg,
81                cont,
82            } => {
83                let result = handler(&effect, &op, &arg);
84                cont(result).interpret(handler)
85            }
86        }
87    }
88    /// Check if this is a pure computation.
89    pub fn is_pure(&self) -> bool {
90        matches!(self, FreerComp::Pure(_))
91    }
92}
93/// The type of a handler: maps input type with effect row to output type.
94pub struct HandlerType {
95    /// The input computation type.
96    pub input_type: String,
97    /// The output computation type.
98    pub output_type: String,
99    /// The effect row being handled.
100    pub effect_row: String,
101}
102impl HandlerType {
103    /// Create a new HandlerType.
104    pub fn new(
105        input_type: impl Into<String>,
106        output_type: impl Into<String>,
107        effect_row: impl Into<String>,
108    ) -> Self {
109        Self {
110            input_type: input_type.into(),
111            output_type: output_type.into(),
112            effect_row: effect_row.into(),
113        }
114    }
115    /// Check whether this handler type handles the given effect.
116    pub fn handles_effect(&self, effect: &str) -> bool {
117        self.effect_row.contains(effect)
118    }
119    /// The continuation type within the handler.
120    pub fn continuation_type(&self) -> String {
121        format!("{} -> Comp {{}} {}", self.input_type, self.output_type)
122    }
123}
124/// A simple static effect row checker that verifies that performed effects
125/// are within the declared effect row.
126#[allow(dead_code)]
127pub struct EffectRowChecker {
128    /// The declared (allowed) effect row.
129    pub declared_row: EffRow,
130    /// Violations found during checking: effect names that were used but not declared.
131    pub violations: Vec<String>,
132}
133#[allow(dead_code)]
134impl EffectRowChecker {
135    /// Create a new checker for a given declared effect row.
136    pub fn new(declared_row: EffRow) -> Self {
137        Self {
138            declared_row,
139            violations: vec![],
140        }
141    }
142    /// Check if the given effect is within the declared row.
143    pub fn check_effect(&mut self, effect: &str) -> bool {
144        if self.declared_row.contains(effect) {
145            true
146        } else {
147            self.violations.push(effect.to_string());
148            false
149        }
150    }
151    /// Check all effects used in a list of operations.
152    pub fn check_all(&mut self, used_effects: &[&str]) -> bool {
153        let mut all_ok = true;
154        for eff in used_effects {
155            if !self.check_effect(eff) {
156                all_ok = false;
157            }
158        }
159        all_ok
160    }
161    /// Return whether any violations were found.
162    pub fn has_violations(&self) -> bool {
163        !self.violations.is_empty()
164    }
165    /// Return a summary of violations.
166    pub fn violation_summary(&self) -> String {
167        if self.violations.is_empty() {
168            "No violations: all effects are within the declared row.".to_string()
169        } else {
170            format!(
171                "Violations: {:?} not in declared row {:?}",
172                self.violations,
173                self.declared_row.effect_names()
174            )
175        }
176    }
177    /// Reset violations (allow re-checking).
178    pub fn reset(&mut self) {
179        self.violations.clear();
180    }
181    /// The declared effect row is a sound over-approximation of used effects.
182    pub fn is_sound_annotation(&self) -> bool {
183        !self.has_violations()
184    }
185}
186/// Tracks usage of linear (one-shot) effects to ensure they are used exactly once.
187#[allow(dead_code)]
188pub struct LinearEffectTracker {
189    /// Map from effect name to usage count.
190    usage: HashMap<String, usize>,
191    /// Effects declared as linear (must be used exactly once).
192    linear_effects: Vec<String>,
193}
194#[allow(dead_code)]
195impl LinearEffectTracker {
196    /// Create a new linear effect tracker.
197    pub fn new(linear_effects: Vec<String>) -> Self {
198        Self {
199            usage: HashMap::new(),
200            linear_effects,
201        }
202    }
203    /// Record one use of the given effect.
204    pub fn use_effect(&mut self, effect: &str) {
205        *self.usage.entry(effect.to_string()).or_insert(0) += 1;
206    }
207    /// Check whether a linear effect was used exactly once.
208    pub fn check_linear(&self, effect: &str) -> bool {
209        self.usage.get(effect).copied().unwrap_or(0) == 1
210    }
211    /// Verify all declared linear effects were used exactly once.
212    pub fn verify_all_linear(&self) -> bool {
213        self.linear_effects.iter().all(|e| self.check_linear(e))
214    }
215    /// Get the usage count of an effect.
216    pub fn usage_count(&self, effect: &str) -> usize {
217        self.usage.get(effect).copied().unwrap_or(0)
218    }
219    /// Return the list of linear effects that were used more than once (violations).
220    pub fn overused_effects(&self) -> Vec<String> {
221        self.linear_effects
222            .iter()
223            .filter(|e| self.usage.get(*e).copied().unwrap_or(0) > 1)
224            .cloned()
225            .collect()
226    }
227    /// Return the list of linear effects that were never used (also violations).
228    pub fn unused_effects(&self) -> Vec<String> {
229        self.linear_effects
230            .iter()
231            .filter(|e| self.usage.get(*e).copied().unwrap_or(0) == 0)
232            .cloned()
233            .collect()
234    }
235    /// Full verification report.
236    pub fn report(&self) -> String {
237        let overused = self.overused_effects();
238        let unused = self.unused_effects();
239        if overused.is_empty() && unused.is_empty() {
240            "Linear effects: all used exactly once (correct)".to_string()
241        } else {
242            format!(
243                "Linear effect violations: overused={:?}, unused={:?}",
244                overused, unused
245            )
246        }
247    }
248}
249/// A value from the free monad over an operation set F.
250/// `FreeMnd<F, A>` is either a pure value A or an F-shaped operation
251/// with a continuation.
252pub enum Free<A> {
253    /// Pure return value.
254    Pure(A),
255    /// An operation (represented by name + argument string) with continuation.
256    Op {
257        /// Effect name.
258        effect: String,
259        /// Operation name.
260        op: String,
261        /// Argument (serialized as string for genericity).
262        arg: String,
263        /// Continuation: given the return value (as string), produce next computation.
264        cont: Box<dyn FnOnce(String) -> Free<A>>,
265    },
266}
267impl<A> Free<A> {
268    /// Construct a pure computation.
269    pub fn pure(val: A) -> Self {
270        Free::Pure(val)
271    }
272    /// Lift a single operation into the free monad.
273    pub fn op(
274        effect: impl Into<String>,
275        op: impl Into<String>,
276        arg: impl Into<String>,
277        cont: impl FnOnce(String) -> Free<A> + 'static,
278    ) -> Self {
279        Free::Op {
280            effect: effect.into(),
281            op: op.into(),
282            arg: arg.into(),
283            cont: Box::new(cont),
284        }
285    }
286    /// Fold over the free monad with a pure handler `ret` and an op handler `alg`.
287    pub fn fold<B: 'static>(
288        self,
289        ret: impl Fn(A) -> B + 'static,
290        alg: &'static dyn Fn(&str, &str, String, Box<dyn FnOnce(String) -> B>) -> B,
291    ) -> B
292    where
293        A: 'static,
294    {
295        match self {
296            Free::Pure(a) => ret(a),
297            Free::Op {
298                effect,
299                op,
300                arg,
301                cont,
302            } => {
303                let ret = std::sync::Arc::new(ret);
304                let ret2 = ret.clone();
305                let cont_b = Box::new(move |s: String| cont(s).fold_inner(ret2, alg));
306                alg(&effect, &op, arg, cont_b)
307            }
308        }
309    }
310    fn fold_inner<B: 'static>(
311        self,
312        ret: std::sync::Arc<impl Fn(A) -> B + 'static>,
313        alg: &'static dyn Fn(&str, &str, String, Box<dyn FnOnce(String) -> B>) -> B,
314    ) -> B
315    where
316        A: 'static,
317    {
318        match self {
319            Free::Pure(a) => ret(a),
320            Free::Op {
321                effect,
322                op,
323                arg,
324                cont,
325            } => {
326                let ret2 = ret.clone();
327                let cont_b = Box::new(move |s: String| cont(s).fold_inner(ret2, alg));
328                alg(&effect, &op, arg, cont_b)
329            }
330        }
331    }
332}
333/// An effect handler that eliminates a specific effect.
334pub struct EffectHandler {
335    /// The name of the handled effect.
336    pub effect: String,
337    /// A description of the handler function.
338    pub handler_fn: String,
339}
340impl EffectHandler {
341    /// Create a new EffectHandler.
342    pub fn new(effect: impl Into<String>, handler_fn: impl Into<String>) -> Self {
343        Self {
344            effect: effect.into(),
345            handler_fn: handler_fn.into(),
346        }
347    }
348    /// A deep handler recursively handles all operations and sub-computations.
349    pub fn deep_handler(&self) -> String {
350        format!(
351            "Deep handler for {}: handles all continuations recursively",
352            self.effect
353        )
354    }
355    /// A shallow handler handles only the first operation encountered.
356    pub fn shallow_handler(&self) -> String {
357        format!(
358            "Shallow handler for {}: one-shot, does not re-handle continuation",
359            self.effect
360        )
361    }
362    /// Handling eliminates the effect from the effect row.
363    pub fn effect_elimination(&self) -> String {
364        format!(
365            "handle[{}]: Comp (E | R) A -> Comp R A via handler {}",
366            self.effect, self.handler_fn
367        )
368    }
369}
370/// An effect row: an ordered (but usually treated as a set) collection of effect signatures.
371#[derive(Debug, Clone, Default)]
372pub struct EffRow {
373    /// The effects present in this row, by name.
374    effects: Vec<String>,
375}
376impl EffRow {
377    /// Create the empty effect row (pure).
378    pub fn empty() -> Self {
379        EffRow { effects: vec![] }
380    }
381    /// Extend this row with one more effect.
382    pub fn extend(&self, eff: impl Into<String>) -> Self {
383        let mut new = self.clone();
384        let name = eff.into();
385        if !new.effects.contains(&name) {
386            new.effects.push(name);
387        }
388        new
389    }
390    /// Check if this row contains the given effect.
391    pub fn contains(&self, eff: &str) -> bool {
392        self.effects.iter().any(|e| e == eff)
393    }
394    /// Check if this row lacks the given effect.
395    pub fn lacks(&self, eff: &str) -> bool {
396        !self.contains(eff)
397    }
398    /// Check if this row is a subset of another.
399    pub fn is_subset_of(&self, other: &EffRow) -> bool {
400        self.effects.iter().all(|e| other.contains(e))
401    }
402    /// Compute the union of two rows.
403    pub fn union(&self, other: &EffRow) -> Self {
404        let mut new = self.clone();
405        for e in &other.effects {
406            if !new.effects.contains(e) {
407                new.effects.push(e.clone());
408            }
409        }
410        new
411    }
412    /// Return the list of effect names.
413    pub fn effect_names(&self) -> &[String] {
414        &self.effects
415    }
416    /// True if the row is empty (pure computation).
417    pub fn is_pure(&self) -> bool {
418        self.effects.is_empty()
419    }
420}
421/// An effect signature: a named collection of operations.
422#[derive(Debug, Clone)]
423pub struct EffSig {
424    /// The name of this effect (e.g., "State", "IO", "Exn").
425    pub name: String,
426    /// The operations provided by this effect.
427    pub operations: Vec<OpDesc>,
428}
429impl EffSig {
430    /// Create a new effect signature.
431    pub fn new(name: impl Into<String>, ops: Vec<OpDesc>) -> Self {
432        EffSig {
433            name: name.into(),
434            operations: ops,
435        }
436    }
437    /// Look up an operation by name.
438    pub fn get_op(&self, op_name: &str) -> Option<&OpDesc> {
439        self.operations.iter().find(|op| op.name == op_name)
440    }
441}
442/// A captured delimited continuation.
443pub struct Continuation<A, B> {
444    func: Box<dyn FnOnce(A) -> B>,
445}
446impl<A, B> Continuation<A, B> {
447    /// Wrap a function as a continuation.
448    pub fn new(f: impl FnOnce(A) -> B + 'static) -> Self {
449        Continuation { func: Box::new(f) }
450    }
451    /// Resume the continuation with a value.
452    pub fn resume(self, val: A) -> B {
453        (self.func)(val)
454    }
455}
456/// An effect-polymorphic computation: parameterized by an effect row variable.
457///
458/// At the Rust level, we represent this as a computation that can be
459/// "instantiated" at a specific effect row.
460pub struct EffPoly<A> {
461    /// The underlying computation, parameterized by an effect row (represented as `EffRow`).
462    compute: Box<dyn Fn(&EffRow) -> A>,
463}
464impl<A> EffPoly<A> {
465    /// Create an effect-polymorphic computation.
466    pub fn new(f: impl Fn(&EffRow) -> A + 'static) -> Self {
467        EffPoly {
468            compute: Box::new(f),
469        }
470    }
471    /// Instantiate at a specific effect row.
472    pub fn instantiate(&self, row: &EffRow) -> A {
473        (self.compute)(row)
474    }
475}
476/// A deep effect handler for a specific effect.
477///
478/// A deep handler handles every operation of the effect recursively,
479/// re-interpreting the continuation in the handled context.
480pub struct DeepHandler<A, B> {
481    /// The name of the handled effect.
482    pub effect_name: String,
483    /// Return clause: `val_clause(a)` handles the final value.
484    pub val_clause: Box<dyn Fn(A) -> B>,
485    /// Operation clauses: map `(op_name, arg, k)` to `B` where `k : String → B`.
486    pub op_clauses: HashMap<String, Box<dyn Fn(String, Box<dyn Fn(String) -> B>) -> B>>,
487}
488impl<A, B: 'static> DeepHandler<A, B> {
489    /// Create a new deep handler.
490    pub fn new(effect_name: impl Into<String>, val_clause: impl Fn(A) -> B + 'static) -> Self {
491        DeepHandler {
492            effect_name: effect_name.into(),
493            val_clause: Box::new(val_clause),
494            op_clauses: HashMap::new(),
495        }
496    }
497    /// Register an operation clause for operation `op_name`.
498    #[allow(clippy::too_many_arguments)]
499    pub fn with_op(
500        mut self,
501        op_name: impl Into<String>,
502        clause: impl Fn(String, Box<dyn Fn(String) -> B>) -> B + 'static,
503    ) -> Self {
504        self.op_clauses.insert(op_name.into(), Box::new(clause));
505        self
506    }
507}
508/// Delimited continuations with a typed prompt.
509pub struct DelimitedContinuation {
510    /// The type of the prompt / answer type.
511    pub prompt_type: String,
512}
513impl DelimitedContinuation {
514    /// Create a new DelimitedContinuation.
515    pub fn new(prompt_type: impl Into<String>) -> Self {
516        Self {
517            prompt_type: prompt_type.into(),
518        }
519    }
520    /// Reset introduces a delimiter for the continuation.
521    pub fn reset(&self) -> String {
522        format!(
523            "reset<{}>: Comp A -> {}",
524            self.prompt_type, self.prompt_type
525        )
526    }
527    /// Shift captures the continuation up to the nearest enclosing reset.
528    pub fn shift(&self) -> String {
529        format!(
530            "shift<{0}>: ((A -> {0}) -> {0}) -> Comp A",
531            self.prompt_type
532        )
533    }
534    /// Delimited continuations are first-class values in this system.
535    pub fn is_first_class(&self) -> bool {
536        true
537    }
538}
539/// The continuation monad supporting call/cc.
540pub struct ContinuationMonad {
541    /// Description of the answer type.
542    pub answer_type: String,
543}
544impl ContinuationMonad {
545    /// Create a new ContinuationMonad.
546    pub fn new(answer_type: impl Into<String>) -> Self {
547        Self {
548            answer_type: answer_type.into(),
549        }
550    }
551    /// Call-with-current-continuation operator.
552    pub fn callcc(&self) -> String {
553        format!(
554            "callcc: ((A -> Cont {} B) -> Cont {} A) -> Cont {} A",
555            self.answer_type, self.answer_type, self.answer_type
556        )
557    }
558    /// The continuation monad satisfies the monad laws.
559    pub fn is_monad(&self) -> bool {
560        true
561    }
562    /// Reset and shift for delimited control.
563    pub fn reset_shift(&self) -> String {
564        format!(
565            "reset: Cont {} {} -> {}; shift: ((A -> {}) -> Cont {} {}) -> Cont {} A",
566            self.answer_type,
567            self.answer_type,
568            self.answer_type,
569            self.answer_type,
570            self.answer_type,
571            self.answer_type,
572            self.answer_type
573        )
574    }
575}
576/// A shallow handler: handles only the first operation encountered.
577pub struct ShallowHandler<A, B> {
578    /// The effect this handler covers.
579    pub effect_name: String,
580    /// Return clause.
581    pub val_clause: Box<dyn Fn(A) -> B>,
582    /// Operation clause: `(op, arg, raw_continuation) → B`.
583    pub op_clause: Box<dyn Fn(String, String, Box<dyn FnOnce(String) -> Free<A>>) -> B>,
584}
585impl<A, B> ShallowHandler<A, B> {
586    /// Create a new shallow handler.
587    pub fn new(
588        effect_name: impl Into<String>,
589        val_clause: impl Fn(A) -> B + 'static,
590        op_clause: impl Fn(String, String, Box<dyn FnOnce(String) -> Free<A>>) -> B + 'static,
591    ) -> Self {
592        ShallowHandler {
593            effect_name: effect_name.into(),
594            val_clause: Box::new(val_clause),
595            op_clause: Box::new(op_clause),
596        }
597    }
598    /// Apply the shallow handler to a computation.
599    pub fn handle(self, comp: Free<A>) -> B {
600        match comp {
601            Free::Pure(a) => (self.val_clause)(a),
602            Free::Op { op, arg, cont, .. } => (self.op_clause)(op, arg, cont),
603        }
604    }
605}
606/// A description of a single algebraic operation in a signature.
607#[derive(Debug, Clone, PartialEq, Eq, Hash)]
608pub struct OpDesc {
609    /// The name of the operation.
610    pub name: String,
611    /// A human-readable description of the parameter type.
612    pub param_ty: String,
613    /// A human-readable description of the return type.
614    pub return_ty: String,
615}
616impl OpDesc {
617    /// Create a new operation description.
618    pub fn new(name: impl Into<String>, param: impl Into<String>, ret: impl Into<String>) -> Self {
619        OpDesc {
620            name: name.into(),
621            param_ty: param.into(),
622            return_ty: ret.into(),
623        }
624    }
625}
626/// An algebraic effect with a name and a list of operations.
627pub struct Effect {
628    /// The name of the effect.
629    pub name: String,
630    /// The operation names of this effect.
631    pub operations: Vec<String>,
632}
633impl Effect {
634    /// Create a new Effect.
635    pub fn new(name: impl Into<String>, operations: Vec<String>) -> Self {
636        Self {
637            name: name.into(),
638            operations,
639        }
640    }
641    /// Every algebraic effect is algebraic in the sense of Plotkin and Power.
642    pub fn is_algebraic(&self) -> bool {
643        true
644    }
645    /// The free monad generated by the functor of operations.
646    pub fn free_monad(&self) -> String {
647        format!(
648            "Free monad for effect {}: Tree over operations {:?}",
649            self.name, self.operations
650        )
651    }
652}
653/// Effect polymorphism — quantifying over effect variables.
654pub struct EffectPolymorphism {
655    /// Type variables.
656    pub type_vars: Vec<String>,
657    /// Effect row variables.
658    pub effect_vars: Vec<String>,
659}
660impl EffectPolymorphism {
661    /// Create a new EffectPolymorphism.
662    pub fn new(type_vars: Vec<String>, effect_vars: Vec<String>) -> Self {
663        Self {
664            type_vars,
665            effect_vars,
666        }
667    }
668    /// Effect abstraction: generalise over an effect variable.
669    pub fn effect_abstraction(&self) -> String {
670        format!(
671            "Lambda {} . body  [abstract over effect rows {:?}]",
672            self.effect_vars.join(", "),
673            self.effect_vars
674        )
675    }
676    /// Effect instantiation: substitute a concrete row for an effect variable.
677    pub fn effect_instantiation(&self) -> String {
678        format!(
679            "Inst [{:?} := R] : substitute concrete effect rows for effect vars",
680            self.effect_vars
681        )
682    }
683}
684/// Simulate shift/reset using a stack-based approach.
685///
686/// `DelimCont<A>` represents a computation that may capture its continuation.
687pub struct DelimCont<A> {
688    body: Box<dyn FnOnce() -> A>,
689}
690impl<A: 'static> DelimCont<A> {
691    /// Create a delimited computation.
692    pub fn new(body: impl FnOnce() -> A + 'static) -> Self {
693        DelimCont {
694            body: Box::new(body),
695        }
696    }
697    /// Run the computation (analogous to `reset`).
698    pub fn reset(self) -> A {
699        (self.body)()
700    }
701}
702/// A row of effects present in a computation type.
703pub struct EffectRow {
704    /// The list of effects in this row.
705    pub effects: Vec<String>,
706}
707impl EffectRow {
708    /// Create a new EffectRow.
709    pub fn new(effects: Vec<String>) -> Self {
710        Self { effects }
711    }
712    /// The empty effect row — pure computations.
713    pub fn is_empty(&self) -> bool {
714        self.effects.is_empty()
715    }
716    /// The union of two effect rows.
717    pub fn union(&self, other: &EffectRow) -> EffectRow {
718        let mut combined = self.effects.clone();
719        for e in &other.effects {
720            if !combined.contains(e) {
721                combined.push(e.clone());
722            }
723        }
724        EffectRow { effects: combined }
725    }
726    /// Remove an effect from the row (effect elimination).
727    pub fn subtract(&self, effect: &str) -> EffectRow {
728        EffectRow {
729            effects: self
730                .effects
731                .iter()
732                .filter(|e| e.as_str() != effect)
733                .cloned()
734                .collect(),
735        }
736    }
737}
738/// A Koka-style effect interpreter that runs a computation by dispatching
739/// each operation to registered handlers.
740pub struct EffectInterpreter {
741    /// Handlers: effect name → op name → handler function (arg_str → result_str).
742    handlers: HashMap<String, HashMap<String, Box<dyn Fn(String) -> String>>>,
743}
744impl EffectInterpreter {
745    /// Create a new effect interpreter.
746    pub fn new() -> Self {
747        EffectInterpreter {
748            handlers: HashMap::new(),
749        }
750    }
751    /// Register a handler for a specific operation of a specific effect.
752    pub fn register(
753        mut self,
754        effect: impl Into<String>,
755        op: impl Into<String>,
756        handler: impl Fn(String) -> String + 'static,
757    ) -> Self {
758        self.handlers
759            .entry(effect.into())
760            .or_default()
761            .insert(op.into(), Box::new(handler));
762        self
763    }
764    /// Run a `Free<String>` computation using the registered handlers.
765    pub fn run(&self, comp: Free<String>) -> String {
766        match comp {
767            Free::Pure(v) => v,
768            Free::Op {
769                effect,
770                op,
771                arg,
772                cont,
773            } => {
774                if let Some(eff_handlers) = self.handlers.get(&effect) {
775                    if let Some(h) = eff_handlers.get(&op) {
776                        let result = h(arg);
777                        let next = cont(result);
778                        self.run(next)
779                    } else {
780                        format!("unhandled_op({}/{})", effect, op)
781                    }
782                } else {
783                    format!("unhandled_effect({})", effect)
784                }
785            }
786        }
787    }
788}
789/// A graded monad tracks resource usage at the type level via grades.
790///
791/// The grade G represents the "cost" or "resource usage" of a computation.
792/// bind composes grades via multiplication: M g a → (a → M h b) → M (g*h) b.
793#[allow(dead_code)]
794pub struct GradedMonadImpl<G, A> {
795    /// The grade (resource annotation) of this computation.
796    pub grade: G,
797    /// The computation value.
798    pub value: A,
799}
800#[allow(dead_code)]
801impl<G: Clone + std::fmt::Debug, A: Clone> GradedMonadImpl<G, A> {
802    /// Lift a value into a graded computation with grade `g`.
803    pub fn unit(grade: G, value: A) -> Self {
804        Self { grade, value }
805    }
806    /// Map a function over the value, preserving the grade.
807    pub fn map<B, F: Fn(A) -> B>(self, f: F) -> GradedMonadImpl<G, B> {
808        GradedMonadImpl {
809            grade: self.grade,
810            value: f(self.value),
811        }
812    }
813    /// Return a description of the grade.
814    pub fn grade_description(&self) -> String {
815        format!("Grade: {:?}", self.grade)
816    }
817    /// Check whether the grade indicates a "pure" (unit-grade) computation.
818    pub fn is_unit_grade(&self) -> bool
819    where
820        G: PartialEq + Default,
821    {
822        self.grade == G::default()
823    }
824}
825/// A simple effect inference engine.
826///
827/// Tracks which effects a computation uses by inspecting `Free` nodes.
828pub struct EffectInferencer {
829    /// Mapping from computation identifiers to their inferred effect rows.
830    inferred: HashMap<String, EffRow>,
831}
832impl EffectInferencer {
833    /// Create a new effect inferencer.
834    pub fn new() -> Self {
835        EffectInferencer {
836            inferred: HashMap::new(),
837        }
838    }
839    /// Infer the effects of a `Free` computation (by collecting effect names).
840    pub fn infer<A>(&mut self, name: impl Into<String>, comp: &Free<A>) -> EffRow {
841        let row = Self::collect_effects(comp);
842        self.inferred.insert(name.into(), row.clone());
843        row
844    }
845    fn collect_effects<A>(comp: &Free<A>) -> EffRow {
846        let mut row = EffRow::empty();
847        Self::collect_rec(comp, &mut row);
848        row
849    }
850    fn collect_rec<A>(comp: &Free<A>, row: &mut EffRow) {
851        match comp {
852            Free::Pure(_) => {}
853            Free::Op { effect, cont, .. } => {
854                row.effects.push(effect.clone());
855                let _ = cont;
856            }
857        }
858    }
859    /// Get the inferred row for a previously analyzed computation.
860    pub fn get_row(&self, name: &str) -> Option<&EffRow> {
861        self.inferred.get(name)
862    }
863}
864/// A pipeline of effect handlers applied in sequence.
865///
866/// Handlers are applied in order: the first handler handles the outermost
867/// effect; the last handler handles the innermost effect (closest to the
868/// computation).
869#[allow(dead_code)]
870pub struct EffectPipeline {
871    /// Ordered list of (effect_name, handler_description) pairs.
872    stages: Vec<(String, String)>,
873}
874#[allow(dead_code)]
875impl EffectPipeline {
876    /// Create an empty effect pipeline.
877    pub fn new() -> Self {
878        Self { stages: vec![] }
879    }
880    /// Append a handler stage for the given effect.
881    pub fn add_stage(mut self, effect: &str, handler_desc: &str) -> Self {
882        self.stages
883            .push((effect.to_string(), handler_desc.to_string()));
884        self
885    }
886    /// The number of handler stages.
887    pub fn depth(&self) -> usize {
888        self.stages.len()
889    }
890    /// The residual effect row after all handlers have been applied.
891    /// (Assumes each stage eliminates exactly one effect.)
892    pub fn residual_row(&self, initial: &EffRow) -> EffRow {
893        let mut row = initial.clone();
894        for (eff, _) in &self.stages {
895            row = EffRow {
896                effects: row.effects.into_iter().filter(|e| e != eff).collect(),
897            };
898        }
899        row
900    }
901    /// Check that the pipeline is complete: no effects remain after all handlers.
902    pub fn is_complete(&self, initial: &EffRow) -> bool {
903        self.residual_row(initial).is_pure()
904    }
905    /// Return a textual description of the pipeline.
906    pub fn describe(&self) -> String {
907        if self.stages.is_empty() {
908            "Empty pipeline (identity)".to_string()
909        } else {
910            let stage_strs: Vec<String> = self
911                .stages
912                .iter()
913                .map(|(eff, desc)| format!("handle[{}] via {}", eff, desc))
914                .collect();
915            stage_strs.join(" >> ")
916        }
917    }
918    /// Verify that the pipeline handles effects in the right order (deep to shallow).
919    pub fn is_valid_deep_order(&self) -> bool {
920        let mut seen = std::collections::HashSet::new();
921        for (eff, _) in &self.stages {
922            if !seen.insert(eff.clone()) {
923                return false;
924            }
925        }
926        true
927    }
928}
929/// The free monad over a functor F.
930pub struct FreeMonad {
931    /// Description of the functor whose free monad is constructed.
932    pub functor: String,
933}
934impl FreeMonad {
935    /// Create a new FreeMonad.
936    pub fn new(functor: impl Into<String>) -> Self {
937        Self {
938            functor: functor.into(),
939        }
940    }
941    /// Unit / return: inject a pure value into the free monad.
942    pub fn unit(&self) -> String {
943        format!("return: A -> Free({}) A", self.functor)
944    }
945    /// Monadic bind: sequencing in the free monad.
946    pub fn bind(&self) -> String {
947        format!(
948            "bind: Free({0}) A -> (A -> Free({0}) B) -> Free({0}) B",
949            self.functor
950        )
951    }
952    /// Interpret: fold the free monad with an algebra.
953    pub fn interpret(&self) -> String {
954        format!(
955            "interpret: (F A -> A) -> Free({}) A -> A  [initial algebra morphism]",
956            self.functor
957        )
958    }
959}