#![allow(missing_docs)]
use super::{Effect};
use crate::diagnostics::{Error as DiagnosticError, Result};
use crate::eval::value::{ThreadSafeEnvironment, Value};
use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, RwLock};
#[derive(Debug, Clone)]
pub enum MonadicValue {
Pure(Value),
IO(IOComputation),
State(StateComputation),
Error(ErrorComputation),
Combined(CombinedComputation),
}
#[derive(Debug, Clone)]
pub struct IOComputation {
action: IOAction,
continuation: Option<Box<MonadicValue>>,
}
#[derive(Debug, Clone)]
pub enum IOAction {
Read(IOSource),
Write(IOTarget, Value),
Print(Value),
WriteValue(Value),
Newline,
OpenRead(String),
OpenWrite(String),
Close(Value),
Return(Value),
Custom(String, Vec<Value>),
}
#[derive(Debug, Clone)]
pub enum IOSource {
Stdin,
File(String),
String(String),
Port(Value),
}
#[derive(Debug, Clone)]
pub enum IOTarget {
Stdout,
Stderr,
File(String),
String(Arc<RwLock<String>>),
Port(Value),
}
#[derive(Debug, Clone)]
pub struct StateComputation {
action: StateAction,
initial_env: Arc<ThreadSafeEnvironment>,
continuation: Option<Box<MonadicValue>>,
}
#[derive(Debug, Clone)]
pub enum StateAction {
Get,
Put(Arc<ThreadSafeEnvironment>),
Modify,
GetVar(String),
SetVar(String, Value),
DefineVar(String, Value),
Return(Value),
Custom(String, Vec<Value>),
}
#[derive(Debug, Clone)]
pub struct ErrorComputation {
action: ErrorAction,
handler: Option<ErrorHandler>,
continuation: Option<Box<MonadicValue>>,
}
#[derive(Debug, Clone)]
pub enum ErrorAction {
Throw(DiagnosticError),
Try(Box<MonadicValue>),
Return(Value),
Custom(String, Vec<Value>),
}
#[derive(Debug, Clone)]
pub struct ErrorHandler {
name: String,
}
#[derive(Debug, Clone)]
pub struct CombinedComputation {
effects: Vec<Effect>,
#[allow(dead_code)]
computations: HashMap<Effect, Box<MonadicValue>>,
primary: Box<MonadicValue>,
}
impl CombinedComputation {
pub fn primary(&self) -> &MonadicValue {
&self.primary
}
}
pub struct Monad;
impl MonadicValue {
pub fn pure(value: Value) -> Self {
MonadicValue::Pure(value)
}
pub fn io(action: IOAction) -> Self {
MonadicValue::IO(IOComputation {
action,
continuation: None,
})
}
pub fn state(action: StateAction, env: Arc<ThreadSafeEnvironment>) -> Self {
MonadicValue::State(StateComputation {
action,
initial_env: env,
continuation: None,
})
}
pub fn error(action: ErrorAction) -> Self {
MonadicValue::Error(ErrorComputation {
action,
handler: None,
continuation: None,
})
}
pub fn effects(&self) -> Vec<Effect> {
match self {
MonadicValue::Pure(_) => vec![Effect::Pure],
MonadicValue::IO(_) => vec![Effect::IO],
MonadicValue::State(_) => vec![Effect::State],
MonadicValue::Error(_) => vec![Effect::Error],
MonadicValue::Combined(comp) => comp.effects.clone(),
}
}
pub fn is_pure(&self) -> bool {
matches!(self, MonadicValue::Pure(_))
}
pub fn into_pure(self) -> Option<Value> {
match self {
MonadicValue::Pure(value) => Some(value),
_ => None,
}
}
pub fn lift_into(self, effect: Effect) -> Self {
if self.effects().contains(&effect) {
return self;
}
match effect {
Effect::Pure => self,
Effect::IO => match self {
MonadicValue::Pure(value) => MonadicValue::io(IOAction::Return(value)),
other => other,
},
Effect::State => match self {
MonadicValue::Pure(value) => {
let env = Arc::new(ThreadSafeEnvironment::new(None, 0));
MonadicValue::state(StateAction::Return(value), env)
},
other => other,
},
Effect::Error => match self {
MonadicValue::Pure(value) => MonadicValue::error(ErrorAction::Return(value)),
other => other,
},
Effect::Custom(_) => {
let mut effects = self.effects();
effects.push(effect);
effects.sort();
effects.dedup();
let mut computations = HashMap::new();
computations.insert(Effect::Pure, Box::new(self.clone()));
MonadicValue::Combined(CombinedComputation {
effects,
computations,
primary: Box::new(self),
})
}
}
}
}
impl Monad {
pub fn return_value(value: Value) -> MonadicValue {
MonadicValue::pure(value)
}
pub fn bind<F>(mv: MonadicValue, f: F) -> MonadicValue
where
F: Fn(Value) -> MonadicValue + 'static,
{
match mv {
MonadicValue::Pure(value) => f(value),
MonadicValue::IO(mut io_comp) => {
io_comp.continuation = Some(Box::new(
MonadicValue::Pure(Value::Unspecified) ));
MonadicValue::IO(io_comp)
},
MonadicValue::State(mut state_comp) => {
state_comp.continuation = Some(Box::new(
MonadicValue::Pure(Value::Unspecified) ));
MonadicValue::State(state_comp)
},
MonadicValue::Error(mut error_comp) => {
error_comp.continuation = Some(Box::new(
MonadicValue::Pure(Value::Unspecified) ));
MonadicValue::Error(error_comp)
},
MonadicValue::Combined(combined) => {
MonadicValue::Combined(combined) }
}
}
pub fn sequence(first: MonadicValue, second: MonadicValue) -> MonadicValue {
Self::bind(first, move |_| second.clone())
}
pub fn join(nested: MonadicValue) -> MonadicValue {
Self::bind(nested, |value| {
MonadicValue::pure(value)
})
}
pub fn fmap<F>(f: F, mv: MonadicValue) -> MonadicValue
where
F: Fn(Value) -> Value + 'static,
{
Self::bind(mv, move |value| MonadicValue::pure(f(value)))
}
pub fn apply(mf: MonadicValue, mv: MonadicValue) -> MonadicValue {
Self::bind(mf, move |_f_val| {
Self::bind(mv.clone(), move |v_val| {
MonadicValue::pure(v_val)
})
})
}
pub fn lift2<F>(f: F, ma: MonadicValue, mb: MonadicValue) -> MonadicValue
where
F: FnOnce(Value, Value) -> Value + Clone + 'static,
{
let f = f.clone();
Self::bind(ma, move |a| {
let f = f.clone();
Self::bind(mb.clone(), move |b| {
let f = f.clone();
MonadicValue::pure(f(a.clone(), b))
})
})
}
pub fn when(condition: bool, action: MonadicValue) -> MonadicValue {
if condition {
action
} else {
MonadicValue::pure(Value::Unspecified)
}
}
pub fn if_then_else(
condition: bool,
then_action: MonadicValue,
else_action: MonadicValue
) -> MonadicValue {
if condition {
then_action
} else {
else_action
}
}
}
impl IOComputation {
pub fn new(action: IOAction) -> Self {
Self {
action,
continuation: None,
}
}
pub fn then(mut self, continuation: MonadicValue) -> Self {
self.continuation = Some(Box::new(continuation));
self
}
pub fn execute(&self) -> Result<Value> {
match &self.action {
IOAction::Return(value) => Ok(value.clone()),
IOAction::Print(value) => {
print!("{}", value.display_string());
Ok(Value::Unspecified)
},
IOAction::WriteValue(value) => {
print!("{value}");
Ok(Value::Unspecified)
},
IOAction::Newline => {
println!();
Ok(Value::Unspecified)
},
IOAction::Write(target, value) => {
match target {
IOTarget::Stdout => {
print!("{value}");
Ok(Value::Unspecified)
},
IOTarget::Stderr => {
eprint!("{value}");
Ok(Value::Unspecified)
},
_ => {
Err(Box::new(DiagnosticError::runtime_error(
"IO target not yet implemented".to_string(),
None,
)))
}
}
},
_ => {
Err(Box::new(DiagnosticError::runtime_error(
"IO action not yet implemented".to_string(),
None,
)))
}
}
}
}
impl StateComputation {
pub fn new(action: StateAction, env: Arc<ThreadSafeEnvironment>) -> Self {
Self {
action,
initial_env: env,
continuation: None,
}
}
pub fn then(mut self, continuation: MonadicValue) -> Self {
self.continuation = Some(Box::new(continuation));
self
}
pub fn execute(&self) -> Result<(Value, Arc<ThreadSafeEnvironment>)> {
match &self.action {
StateAction::Return(value) => Ok((value.clone(), self.initial_env.clone())),
StateAction::Get => {
Ok((Value::Unspecified, self.initial_env.clone()))
},
StateAction::Put(new_env) => {
Ok((Value::Unspecified, new_env.clone()))
},
StateAction::GetVar(name) => {
match self.initial_env.lookup(name) {
Some(value) => Ok((value, self.initial_env.clone())),
None => Err(Box::new(DiagnosticError::runtime_error(
format!("Unbound variable: {name}"),
None,
))),
}
},
StateAction::SetVar(name, value) => {
if let Some(new_env) = self.initial_env.set_cow(name, value.clone()) {
Ok((Value::Unspecified, new_env))
} else {
Err(Box::new(DiagnosticError::runtime_error(
format!("Variable {name} not found for setting"),
None,
)))
}
},
StateAction::DefineVar(name, value) => {
let new_env = self.initial_env.define_cow(name.clone(), value.clone());
Ok((Value::Unspecified, new_env))
},
_ => {
Err(Box::new(DiagnosticError::runtime_error(
"State action not yet implemented".to_string(),
None,
)))
}
}
}
}
impl ErrorComputation {
pub fn new(action: ErrorAction) -> Self {
Self {
action,
handler: None,
continuation: None,
}
}
pub fn with_handler(mut self, handler_name: String) -> Self {
self.handler = Some(ErrorHandler {
name: handler_name,
});
self
}
pub fn then(mut self, continuation: MonadicValue) -> Self {
self.continuation = Some(Box::new(continuation));
self
}
pub fn execute(&self) -> Result<Value> {
match &self.action {
ErrorAction::Return(value) => Ok(value.clone()),
ErrorAction::Throw(error) => {
if let Some(_handler) = &self.handler {
Ok(Value::string("Error handled".to_string()))
} else {
Err(Box::new(error.clone()))
}
},
ErrorAction::Try(computation) => {
match computation.as_ref() {
MonadicValue::Pure(value) => Ok(value.clone()),
_ => {
Ok(Value::Unspecified)
}
}
},
_ => {
Err(Box::new(DiagnosticError::runtime_error(
"Error action not yet implemented".to_string(),
None,
)))
}
}
}
}
impl PartialEq for StateAction {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(StateAction::Get, StateAction::Get) => true,
(StateAction::Put(a), StateAction::Put(b)) => Arc::ptr_eq(a, b),
(StateAction::GetVar(a), StateAction::GetVar(b)) => a == b,
(StateAction::SetVar(a1, v1), StateAction::SetVar(a2, v2)) => a1 == a2 && v1 == v2,
(StateAction::DefineVar(a1, v1), StateAction::DefineVar(a2, v2)) => a1 == a2 && v1 == v2,
(StateAction::Return(a), StateAction::Return(b)) => a == b,
(StateAction::Custom(a1, v1), StateAction::Custom(a2, v2)) => a1 == a2 && v1 == v2,
_ => false,
}
}
}
impl PartialEq for ErrorHandler {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl fmt::Display for MonadicValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MonadicValue::Pure(value) => write!(f, "Pure({value})"),
MonadicValue::IO(_) => write!(f, "IO(<computation>)"),
MonadicValue::State(_) => write!(f, "State(<computation>)"),
MonadicValue::Error(_) => write!(f, "Error(<computation>)"),
MonadicValue::Combined(comp) => {
write!(f, "Combined({:?}, <computation>)", comp.effects)
}
}
}
}
impl fmt::Display for IOAction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IOAction::Read(_) => write!(f, "Read"),
IOAction::Write(_, value) => write!(f, "Write({value})"),
IOAction::Print(value) => write!(f, "Print({value})"),
IOAction::WriteValue(value) => write!(f, "WriteValue({value})"),
IOAction::Newline => write!(f, "Newline"),
IOAction::Return(value) => write!(f, "Return({value})"),
IOAction::Custom(name, _) => write!(f, "Custom({name})"),
_ => write!(f, "<IO action>"),
}
}
}
impl fmt::Display for StateAction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StateAction::Get => write!(f, "Get"),
StateAction::Put(_) => write!(f, "Put"),
StateAction::GetVar(name) => write!(f, "GetVar({name})"),
StateAction::SetVar(name, value) => write!(f, "SetVar({name}, {value})"),
StateAction::DefineVar(name, value) => write!(f, "DefineVar({name}, {value})"),
StateAction::Return(value) => write!(f, "Return({value})"),
StateAction::Custom(name, _) => write!(f, "Custom({name})"),
_ => write!(f, "<State action>"),
}
}
}
unsafe impl Send for MonadicValue {}
unsafe impl Sync for MonadicValue {}
unsafe impl Send for IOComputation {}
unsafe impl Sync for IOComputation {}
unsafe impl Send for StateComputation {}
unsafe impl Sync for StateComputation {}
unsafe impl Send for ErrorComputation {}
unsafe impl Sync for ErrorComputation {}
unsafe impl Send for CombinedComputation {}
unsafe impl Sync for CombinedComputation {}
unsafe impl Send for IOAction {}
unsafe impl Sync for IOAction {}
unsafe impl Send for IOSource {}
unsafe impl Sync for IOSource {}
unsafe impl Send for IOTarget {}
unsafe impl Sync for IOTarget {}
unsafe impl Send for StateAction {}
unsafe impl Sync for StateAction {}
unsafe impl Send for ErrorAction {}
unsafe impl Sync for ErrorAction {}
unsafe impl Send for ErrorHandler {}
unsafe impl Sync for ErrorHandler {}