use std::collections::HashMap;
use super::functions::*;
use std::fmt;
pub struct AlgebraicEffect {
pub signature: Vec<(String, String, String)>,
}
impl AlgebraicEffect {
pub fn new(signature: Vec<(String, String, String)>) -> Self {
Self { signature }
}
pub fn effect_theory(&self) -> String {
format!(
"Algebraic theory with {} operations; models are algebras satisfying handler equations",
self.signature.len()
)
}
pub fn operations_are_equations(&self) -> bool {
true
}
}
#[allow(dead_code)]
pub enum FreerComp {
Pure(String),
Impure {
effect: String,
op: String,
arg: String,
cont: Box<dyn FnOnce(String) -> FreerComp>,
},
}
#[allow(dead_code)]
impl FreerComp {
pub fn pure(v: impl Into<String>) -> Self {
FreerComp::Pure(v.into())
}
pub fn impure(
effect: impl Into<String>,
op: impl Into<String>,
arg: impl Into<String>,
cont: impl FnOnce(String) -> FreerComp + 'static,
) -> Self {
FreerComp::Impure {
effect: effect.into(),
op: op.into(),
arg: arg.into(),
cont: Box::new(cont),
}
}
pub fn interpret(self, handler: &dyn Fn(&str, &str, &str) -> String) -> String {
match self {
FreerComp::Pure(v) => v,
FreerComp::Impure {
effect,
op,
arg,
cont,
} => {
let result = handler(&effect, &op, &arg);
cont(result).interpret(handler)
}
}
}
pub fn is_pure(&self) -> bool {
matches!(self, FreerComp::Pure(_))
}
}
pub struct HandlerType {
pub input_type: String,
pub output_type: String,
pub effect_row: String,
}
impl HandlerType {
pub fn new(
input_type: impl Into<String>,
output_type: impl Into<String>,
effect_row: impl Into<String>,
) -> Self {
Self {
input_type: input_type.into(),
output_type: output_type.into(),
effect_row: effect_row.into(),
}
}
pub fn handles_effect(&self, effect: &str) -> bool {
self.effect_row.contains(effect)
}
pub fn continuation_type(&self) -> String {
format!("{} -> Comp {{}} {}", self.input_type, self.output_type)
}
}
#[allow(dead_code)]
pub struct EffectRowChecker {
pub declared_row: EffRow,
pub violations: Vec<String>,
}
#[allow(dead_code)]
impl EffectRowChecker {
pub fn new(declared_row: EffRow) -> Self {
Self {
declared_row,
violations: vec![],
}
}
pub fn check_effect(&mut self, effect: &str) -> bool {
if self.declared_row.contains(effect) {
true
} else {
self.violations.push(effect.to_string());
false
}
}
pub fn check_all(&mut self, used_effects: &[&str]) -> bool {
let mut all_ok = true;
for eff in used_effects {
if !self.check_effect(eff) {
all_ok = false;
}
}
all_ok
}
pub fn has_violations(&self) -> bool {
!self.violations.is_empty()
}
pub fn violation_summary(&self) -> String {
if self.violations.is_empty() {
"No violations: all effects are within the declared row.".to_string()
} else {
format!(
"Violations: {:?} not in declared row {:?}",
self.violations,
self.declared_row.effect_names()
)
}
}
pub fn reset(&mut self) {
self.violations.clear();
}
pub fn is_sound_annotation(&self) -> bool {
!self.has_violations()
}
}
#[allow(dead_code)]
pub struct LinearEffectTracker {
usage: HashMap<String, usize>,
linear_effects: Vec<String>,
}
#[allow(dead_code)]
impl LinearEffectTracker {
pub fn new(linear_effects: Vec<String>) -> Self {
Self {
usage: HashMap::new(),
linear_effects,
}
}
pub fn use_effect(&mut self, effect: &str) {
*self.usage.entry(effect.to_string()).or_insert(0) += 1;
}
pub fn check_linear(&self, effect: &str) -> bool {
self.usage.get(effect).copied().unwrap_or(0) == 1
}
pub fn verify_all_linear(&self) -> bool {
self.linear_effects.iter().all(|e| self.check_linear(e))
}
pub fn usage_count(&self, effect: &str) -> usize {
self.usage.get(effect).copied().unwrap_or(0)
}
pub fn overused_effects(&self) -> Vec<String> {
self.linear_effects
.iter()
.filter(|e| self.usage.get(*e).copied().unwrap_or(0) > 1)
.cloned()
.collect()
}
pub fn unused_effects(&self) -> Vec<String> {
self.linear_effects
.iter()
.filter(|e| self.usage.get(*e).copied().unwrap_or(0) == 0)
.cloned()
.collect()
}
pub fn report(&self) -> String {
let overused = self.overused_effects();
let unused = self.unused_effects();
if overused.is_empty() && unused.is_empty() {
"Linear effects: all used exactly once (correct)".to_string()
} else {
format!(
"Linear effect violations: overused={:?}, unused={:?}",
overused, unused
)
}
}
}
pub enum Free<A> {
Pure(A),
Op {
effect: String,
op: String,
arg: String,
cont: Box<dyn FnOnce(String) -> Free<A>>,
},
}
impl<A> Free<A> {
pub fn pure(val: A) -> Self {
Free::Pure(val)
}
pub fn op(
effect: impl Into<String>,
op: impl Into<String>,
arg: impl Into<String>,
cont: impl FnOnce(String) -> Free<A> + 'static,
) -> Self {
Free::Op {
effect: effect.into(),
op: op.into(),
arg: arg.into(),
cont: Box::new(cont),
}
}
pub fn fold<B: 'static>(
self,
ret: impl Fn(A) -> B + 'static,
alg: &'static dyn Fn(&str, &str, String, Box<dyn FnOnce(String) -> B>) -> B,
) -> B
where
A: 'static,
{
match self {
Free::Pure(a) => ret(a),
Free::Op {
effect,
op,
arg,
cont,
} => {
let ret = std::sync::Arc::new(ret);
let ret2 = ret.clone();
let cont_b = Box::new(move |s: String| cont(s).fold_inner(ret2, alg));
alg(&effect, &op, arg, cont_b)
}
}
}
fn fold_inner<B: 'static>(
self,
ret: std::sync::Arc<impl Fn(A) -> B + 'static>,
alg: &'static dyn Fn(&str, &str, String, Box<dyn FnOnce(String) -> B>) -> B,
) -> B
where
A: 'static,
{
match self {
Free::Pure(a) => ret(a),
Free::Op {
effect,
op,
arg,
cont,
} => {
let ret2 = ret.clone();
let cont_b = Box::new(move |s: String| cont(s).fold_inner(ret2, alg));
alg(&effect, &op, arg, cont_b)
}
}
}
}
pub struct EffectHandler {
pub effect: String,
pub handler_fn: String,
}
impl EffectHandler {
pub fn new(effect: impl Into<String>, handler_fn: impl Into<String>) -> Self {
Self {
effect: effect.into(),
handler_fn: handler_fn.into(),
}
}
pub fn deep_handler(&self) -> String {
format!(
"Deep handler for {}: handles all continuations recursively",
self.effect
)
}
pub fn shallow_handler(&self) -> String {
format!(
"Shallow handler for {}: one-shot, does not re-handle continuation",
self.effect
)
}
pub fn effect_elimination(&self) -> String {
format!(
"handle[{}]: Comp (E | R) A -> Comp R A via handler {}",
self.effect, self.handler_fn
)
}
}
#[derive(Debug, Clone, Default)]
pub struct EffRow {
effects: Vec<String>,
}
impl EffRow {
pub fn empty() -> Self {
EffRow { effects: vec![] }
}
pub fn extend(&self, eff: impl Into<String>) -> Self {
let mut new = self.clone();
let name = eff.into();
if !new.effects.contains(&name) {
new.effects.push(name);
}
new
}
pub fn contains(&self, eff: &str) -> bool {
self.effects.iter().any(|e| e == eff)
}
pub fn lacks(&self, eff: &str) -> bool {
!self.contains(eff)
}
pub fn is_subset_of(&self, other: &EffRow) -> bool {
self.effects.iter().all(|e| other.contains(e))
}
pub fn union(&self, other: &EffRow) -> Self {
let mut new = self.clone();
for e in &other.effects {
if !new.effects.contains(e) {
new.effects.push(e.clone());
}
}
new
}
pub fn effect_names(&self) -> &[String] {
&self.effects
}
pub fn is_pure(&self) -> bool {
self.effects.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct EffSig {
pub name: String,
pub operations: Vec<OpDesc>,
}
impl EffSig {
pub fn new(name: impl Into<String>, ops: Vec<OpDesc>) -> Self {
EffSig {
name: name.into(),
operations: ops,
}
}
pub fn get_op(&self, op_name: &str) -> Option<&OpDesc> {
self.operations.iter().find(|op| op.name == op_name)
}
}
pub struct Continuation<A, B> {
func: Box<dyn FnOnce(A) -> B>,
}
impl<A, B> Continuation<A, B> {
pub fn new(f: impl FnOnce(A) -> B + 'static) -> Self {
Continuation { func: Box::new(f) }
}
pub fn resume(self, val: A) -> B {
(self.func)(val)
}
}
pub struct EffPoly<A> {
compute: Box<dyn Fn(&EffRow) -> A>,
}
impl<A> EffPoly<A> {
pub fn new(f: impl Fn(&EffRow) -> A + 'static) -> Self {
EffPoly {
compute: Box::new(f),
}
}
pub fn instantiate(&self, row: &EffRow) -> A {
(self.compute)(row)
}
}
pub struct DeepHandler<A, B> {
pub effect_name: String,
pub val_clause: Box<dyn Fn(A) -> B>,
pub op_clauses: HashMap<String, Box<dyn Fn(String, Box<dyn Fn(String) -> B>) -> B>>,
}
impl<A, B: 'static> DeepHandler<A, B> {
pub fn new(effect_name: impl Into<String>, val_clause: impl Fn(A) -> B + 'static) -> Self {
DeepHandler {
effect_name: effect_name.into(),
val_clause: Box::new(val_clause),
op_clauses: HashMap::new(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn with_op(
mut self,
op_name: impl Into<String>,
clause: impl Fn(String, Box<dyn Fn(String) -> B>) -> B + 'static,
) -> Self {
self.op_clauses.insert(op_name.into(), Box::new(clause));
self
}
}
pub struct DelimitedContinuation {
pub prompt_type: String,
}
impl DelimitedContinuation {
pub fn new(prompt_type: impl Into<String>) -> Self {
Self {
prompt_type: prompt_type.into(),
}
}
pub fn reset(&self) -> String {
format!(
"reset<{}>: Comp A -> {}",
self.prompt_type, self.prompt_type
)
}
pub fn shift(&self) -> String {
format!(
"shift<{0}>: ((A -> {0}) -> {0}) -> Comp A",
self.prompt_type
)
}
pub fn is_first_class(&self) -> bool {
true
}
}
pub struct ContinuationMonad {
pub answer_type: String,
}
impl ContinuationMonad {
pub fn new(answer_type: impl Into<String>) -> Self {
Self {
answer_type: answer_type.into(),
}
}
pub fn callcc(&self) -> String {
format!(
"callcc: ((A -> Cont {} B) -> Cont {} A) -> Cont {} A",
self.answer_type, self.answer_type, self.answer_type
)
}
pub fn is_monad(&self) -> bool {
true
}
pub fn reset_shift(&self) -> String {
format!(
"reset: Cont {} {} -> {}; shift: ((A -> {}) -> Cont {} {}) -> Cont {} A",
self.answer_type,
self.answer_type,
self.answer_type,
self.answer_type,
self.answer_type,
self.answer_type,
self.answer_type
)
}
}
pub struct ShallowHandler<A, B> {
pub effect_name: String,
pub val_clause: Box<dyn Fn(A) -> B>,
pub op_clause: Box<dyn Fn(String, String, Box<dyn FnOnce(String) -> Free<A>>) -> B>,
}
impl<A, B> ShallowHandler<A, B> {
pub fn new(
effect_name: impl Into<String>,
val_clause: impl Fn(A) -> B + 'static,
op_clause: impl Fn(String, String, Box<dyn FnOnce(String) -> Free<A>>) -> B + 'static,
) -> Self {
ShallowHandler {
effect_name: effect_name.into(),
val_clause: Box::new(val_clause),
op_clause: Box::new(op_clause),
}
}
pub fn handle(self, comp: Free<A>) -> B {
match comp {
Free::Pure(a) => (self.val_clause)(a),
Free::Op { op, arg, cont, .. } => (self.op_clause)(op, arg, cont),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct OpDesc {
pub name: String,
pub param_ty: String,
pub return_ty: String,
}
impl OpDesc {
pub fn new(name: impl Into<String>, param: impl Into<String>, ret: impl Into<String>) -> Self {
OpDesc {
name: name.into(),
param_ty: param.into(),
return_ty: ret.into(),
}
}
}
pub struct Effect {
pub name: String,
pub operations: Vec<String>,
}
impl Effect {
pub fn new(name: impl Into<String>, operations: Vec<String>) -> Self {
Self {
name: name.into(),
operations,
}
}
pub fn is_algebraic(&self) -> bool {
true
}
pub fn free_monad(&self) -> String {
format!(
"Free monad for effect {}: Tree over operations {:?}",
self.name, self.operations
)
}
}
pub struct EffectPolymorphism {
pub type_vars: Vec<String>,
pub effect_vars: Vec<String>,
}
impl EffectPolymorphism {
pub fn new(type_vars: Vec<String>, effect_vars: Vec<String>) -> Self {
Self {
type_vars,
effect_vars,
}
}
pub fn effect_abstraction(&self) -> String {
format!(
"Lambda {} . body [abstract over effect rows {:?}]",
self.effect_vars.join(", "),
self.effect_vars
)
}
pub fn effect_instantiation(&self) -> String {
format!(
"Inst [{:?} := R] : substitute concrete effect rows for effect vars",
self.effect_vars
)
}
}
pub struct DelimCont<A> {
body: Box<dyn FnOnce() -> A>,
}
impl<A: 'static> DelimCont<A> {
pub fn new(body: impl FnOnce() -> A + 'static) -> Self {
DelimCont {
body: Box::new(body),
}
}
pub fn reset(self) -> A {
(self.body)()
}
}
pub struct EffectRow {
pub effects: Vec<String>,
}
impl EffectRow {
pub fn new(effects: Vec<String>) -> Self {
Self { effects }
}
pub fn is_empty(&self) -> bool {
self.effects.is_empty()
}
pub fn union(&self, other: &EffectRow) -> EffectRow {
let mut combined = self.effects.clone();
for e in &other.effects {
if !combined.contains(e) {
combined.push(e.clone());
}
}
EffectRow { effects: combined }
}
pub fn subtract(&self, effect: &str) -> EffectRow {
EffectRow {
effects: self
.effects
.iter()
.filter(|e| e.as_str() != effect)
.cloned()
.collect(),
}
}
}
pub struct EffectInterpreter {
handlers: HashMap<String, HashMap<String, Box<dyn Fn(String) -> String>>>,
}
impl EffectInterpreter {
pub fn new() -> Self {
EffectInterpreter {
handlers: HashMap::new(),
}
}
pub fn register(
mut self,
effect: impl Into<String>,
op: impl Into<String>,
handler: impl Fn(String) -> String + 'static,
) -> Self {
self.handlers
.entry(effect.into())
.or_default()
.insert(op.into(), Box::new(handler));
self
}
pub fn run(&self, comp: Free<String>) -> String {
match comp {
Free::Pure(v) => v,
Free::Op {
effect,
op,
arg,
cont,
} => {
if let Some(eff_handlers) = self.handlers.get(&effect) {
if let Some(h) = eff_handlers.get(&op) {
let result = h(arg);
let next = cont(result);
self.run(next)
} else {
format!("unhandled_op({}/{})", effect, op)
}
} else {
format!("unhandled_effect({})", effect)
}
}
}
}
}
#[allow(dead_code)]
pub struct GradedMonadImpl<G, A> {
pub grade: G,
pub value: A,
}
#[allow(dead_code)]
impl<G: Clone + std::fmt::Debug, A: Clone> GradedMonadImpl<G, A> {
pub fn unit(grade: G, value: A) -> Self {
Self { grade, value }
}
pub fn map<B, F: Fn(A) -> B>(self, f: F) -> GradedMonadImpl<G, B> {
GradedMonadImpl {
grade: self.grade,
value: f(self.value),
}
}
pub fn grade_description(&self) -> String {
format!("Grade: {:?}", self.grade)
}
pub fn is_unit_grade(&self) -> bool
where
G: PartialEq + Default,
{
self.grade == G::default()
}
}
pub struct EffectInferencer {
inferred: HashMap<String, EffRow>,
}
impl EffectInferencer {
pub fn new() -> Self {
EffectInferencer {
inferred: HashMap::new(),
}
}
pub fn infer<A>(&mut self, name: impl Into<String>, comp: &Free<A>) -> EffRow {
let row = Self::collect_effects(comp);
self.inferred.insert(name.into(), row.clone());
row
}
fn collect_effects<A>(comp: &Free<A>) -> EffRow {
let mut row = EffRow::empty();
Self::collect_rec(comp, &mut row);
row
}
fn collect_rec<A>(comp: &Free<A>, row: &mut EffRow) {
match comp {
Free::Pure(_) => {}
Free::Op { effect, cont, .. } => {
row.effects.push(effect.clone());
let _ = cont;
}
}
}
pub fn get_row(&self, name: &str) -> Option<&EffRow> {
self.inferred.get(name)
}
}
#[allow(dead_code)]
pub struct EffectPipeline {
stages: Vec<(String, String)>,
}
#[allow(dead_code)]
impl EffectPipeline {
pub fn new() -> Self {
Self { stages: vec![] }
}
pub fn add_stage(mut self, effect: &str, handler_desc: &str) -> Self {
self.stages
.push((effect.to_string(), handler_desc.to_string()));
self
}
pub fn depth(&self) -> usize {
self.stages.len()
}
pub fn residual_row(&self, initial: &EffRow) -> EffRow {
let mut row = initial.clone();
for (eff, _) in &self.stages {
row = EffRow {
effects: row.effects.into_iter().filter(|e| e != eff).collect(),
};
}
row
}
pub fn is_complete(&self, initial: &EffRow) -> bool {
self.residual_row(initial).is_pure()
}
pub fn describe(&self) -> String {
if self.stages.is_empty() {
"Empty pipeline (identity)".to_string()
} else {
let stage_strs: Vec<String> = self
.stages
.iter()
.map(|(eff, desc)| format!("handle[{}] via {}", eff, desc))
.collect();
stage_strs.join(" >> ")
}
}
pub fn is_valid_deep_order(&self) -> bool {
let mut seen = std::collections::HashSet::new();
for (eff, _) in &self.stages {
if !seen.insert(eff.clone()) {
return false;
}
}
true
}
}
pub struct FreeMonad {
pub functor: String,
}
impl FreeMonad {
pub fn new(functor: impl Into<String>) -> Self {
Self {
functor: functor.into(),
}
}
pub fn unit(&self) -> String {
format!("return: A -> Free({}) A", self.functor)
}
pub fn bind(&self) -> String {
format!(
"bind: Free({0}) A -> (A -> Free({0}) B) -> Free({0}) B",
self.functor
)
}
pub fn interpret(&self) -> String {
format!(
"interpret: (F A -> A) -> Free({}) A -> A [initial algebra morphism]",
self.functor
)
}
}