use std::cell::RefCell;
use std::collections::HashSet;
use std::rc::Rc;
use crate::Name;
#[derive(Clone, Debug)]
pub struct FreshState {
counter: usize,
used_names: HashSet<String>,
}
impl FreshState {
pub fn new() -> Self {
FreshState {
counter: 0,
used_names: HashSet::new(),
}
}
}
impl Default for FreshState {
fn default() -> Self {
Self::new()
}
}
pub struct FreshM<T> {
computation: Box<dyn FnOnce(Rc<RefCell<FreshState>>) -> T>,
}
impl<T> FreshM<T> {
pub fn new<F>(f: F) -> Self
where
F: FnOnce(Rc<RefCell<FreshState>>) -> T + 'static, {
FreshM {
computation: Box::new(f),
}
}
pub fn pure(value: T) -> Self
where
T: 'static, {
FreshM::new(move |_| value)
}
pub fn run_fresh(self) -> T {
let state = Rc::new(RefCell::new(FreshState::new()));
(self.computation)(state)
}
pub fn run_with_state(self, state: Rc<RefCell<FreshState>>) -> T {
(self.computation)(state)
}
pub fn map<U, F>(self, f: F) -> FreshM<U>
where
F: FnOnce(T) -> U + 'static,
T: 'static, {
FreshM::new(move |state| {
let result = self.run_with_state(state.clone());
f(result)
})
}
pub fn flat_map<U, F>(self, f: F) -> FreshM<U>
where
F: FnOnce(T) -> FreshM<U> + 'static,
T: 'static,
U: 'static, {
FreshM::new(move |state| {
let result = self.run_with_state(state.clone());
let next = f(result);
next.run_with_state(state)
})
}
pub fn and_then<U, F>(self, f: F) -> FreshM<U>
where
F: FnOnce(T) -> FreshM<U> + 'static,
T: 'static,
U: 'static, {
self.flat_map(f)
}
}
pub trait Fresh {
fn fresh(&self) -> FreshM<Self>
where
Self: Sized;
}
impl<T> Fresh for Name<T> {
fn fresh(&self) -> FreshM<Self> {
let base = self.string().to_string();
FreshM::new(move |state| {
let mut st = state.borrow_mut();
let mut candidate = base.clone();
let mut suffix = 0;
while st.used_names.contains(&candidate) {
suffix += 1;
candidate = format!("{}{}", base, suffix);
}
st.used_names.insert(candidate.clone());
st.counter += 1;
Name::with_index(candidate, st.counter - 1)
})
}
}
pub fn run_fresh<T>(computation: FreshM<T>) -> T {
computation.run_fresh()
}