use crate::eval::{
Value, Environment,
monadic_architecture::{
MonadicComputation, EffectInterpreter, InterpreterConfiguration,
},
};
use crate::effects::{
Effect, EffectContext, EffectResult, EffectHandler,
IO, IOContext, FileMode, FileHandle,
State, Reader, Maybe, Either,
EffectfulComputation,
};
use crate::diagnostics::{Result, Error, Span};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::rc::Rc;
use async_trait::async_trait;
#[cfg(feature = "async-runtime")]
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[derive(Debug, Default)]
pub struct UnifiedEffectRegistry {
handlers: HashMap<String, Arc<dyn UnifiedEffectHandler + Send + Sync>>,
fallback_handler: Option<Arc<dyn UnifiedEffectHandler + Send + Sync>>,
config: EffectRegistryConfiguration,
}
#[derive(Debug, Clone)]
pub struct EffectRegistryConfiguration {
pub enable_effect_chaining: bool,
pub max_chain_depth: usize,
pub enable_parallel_effects: bool,
pub effect_timeout_ms: u64,
}
#[async_trait]
pub trait UnifiedEffectHandler: std::fmt::Debug + Send + Sync {
async fn handle_effect(
&self,
effect: &Effect,
args: &[Value],
context: &EffectContext,
) -> Result<MonadicComputation<Value>>;
fn effect_name(&self) -> &str;
fn can_handle(&self, effect: &Effect) -> bool;
fn priority(&self) -> i32 {
0
}
async fn initialize(&self) -> Result<()> {
Ok(())
}
async fn cleanup(&self) -> Result<()> {
Ok(())
}
fn metadata(&self) -> EffectHandlerMetadata {
EffectHandlerMetadata {
name: self.effect_name().to_string(),
version: "1.0.0".to_string(),
description: "Generic effect handler".to_string(),
supported_effects: vec![],
capabilities: vec![],
}
}
}
#[derive(Debug, Clone)]
pub struct EffectHandlerMetadata {
pub name: String,
pub version: String,
pub description: String,
pub supported_effects: Vec<Effect>,
pub capabilities: Vec<EffectCapability>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EffectCapability {
AsyncSupport,
Stateful,
Composable,
Transactional,
Streaming,
Custom(String),
}
#[derive(Debug)]
pub struct MonadicIOHandler {
io_context: Arc<Mutex<IOContext>>,
config: IOHandlerConfiguration,
}
#[derive(Debug, Clone)]
pub struct IOHandlerConfiguration {
pub enable_file_operations: bool,
pub enable_network_operations: bool,
pub default_buffer_size: usize,
pub io_timeout_ms: u64,
}
#[derive(Debug, Default)]
pub struct MonadicStateHandler {
state: Arc<Mutex<HashMap<String, Value>>>,
config: StateHandlerConfiguration,
}
#[derive(Debug, Clone)]
pub struct StateHandlerConfiguration {
pub enable_transactions: bool,
pub max_state_keys: usize,
pub enable_persistence: bool,
}
#[derive(Debug, Default)]
pub struct MonadicErrorHandler {
config: ErrorHandlerConfiguration,
}
#[derive(Debug, Clone)]
pub struct ErrorHandlerConfiguration {
pub capture_stack_traces: bool,
pub enable_error_recovery: bool,
pub max_error_chain_depth: usize,
}
#[derive(Debug, Default)]
pub struct MonadicMaybeHandler {
config: MaybeHandlerConfiguration,
}
#[derive(Debug, Clone)]
pub struct MaybeHandlerConfiguration {
pub nothing_behavior: NothingBehavior,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum NothingBehavior {
#[default]
ReturnNothing,
ThrowError,
UseDefault(Value),
}
#[derive(Debug)]
pub struct CompositeEffectHandler {
handlers: Vec<Arc<dyn UnifiedEffectHandler + Send + Sync>>,
config: CompositeHandlerConfiguration,
}
#[derive(Debug, Clone)]
pub struct CompositeHandlerConfiguration {
pub short_circuit: bool,
pub collect_all_results: bool,
pub combination_strategy: CombinationStrategy,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CombinationStrategy {
First,
Last,
Collect,
Custom,
}
impl UnifiedEffectRegistry {
pub fn new() -> Self {
Self {
handlers: HashMap::new(),
fallback_handler: None,
config: EffectRegistryConfiguration::default(),
}
}
pub fn with_standard_handlers() -> Self {
let mut registry = Self::new();
let io_handler = Arc::new(MonadicIOHandler::new());
let state_handler = Arc::new(MonadicStateHandler::new());
let error_handler = Arc::new(MonadicErrorHandler::new());
let maybe_handler = Arc::new(MonadicMaybeHandler::new());
registry.register_handler("io".to_string(), io_handler);
registry.register_handler("state".to_string(), state_handler);
registry.register_handler("error".to_string(), error_handler);
registry.register_handler("maybe".to_string(), maybe_handler);
registry
}
pub fn register_handler(
&mut self,
name: String,
handler: Arc<dyn UnifiedEffectHandler + Send + Sync>,
) {
self.handlers.insert(name, handler);
}
pub fn find_handler(&self, effect: &Effect) -> Option<Arc<dyn UnifiedEffectHandler + Send + Sync>> {
for handler in self.handlers.values() {
if handler.can_handle(effect) {
return Some(handler.clone());
}
}
self.fallback_handler.clone()
}
pub async fn handle_effect(
&self,
effect: &Effect,
args: &[Value],
context: &EffectContext,
) -> Result<MonadicComputation<Value>> {
if let Some(handler) = self.find_handler(effect) {
handler.handle_effect(effect, args, context).await
} else {
Err(Box::new(Error::runtime_error(
format!("No handler found for effect: {effect:?}"),
None,
)))
}
}
pub fn list_handlers(&self) -> Vec<String> {
self.handlers.keys().cloned().collect()
}
pub fn config(&self) -> &EffectRegistryConfiguration {
&self.config
}
}
impl MonadicIOHandler {
pub fn new() -> Self {
Self {
io_context: Arc::new(Mutex::new(IOContext::new())),
config: IOHandlerConfiguration::default(),
}
}
pub fn with_config(config: IOHandlerConfiguration) -> Self {
Self {
io_context: Arc::new(Mutex::new(IOContext::new())),
config,
}
}
}
impl Default for MonadicIOHandler {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl UnifiedEffectHandler for MonadicIOHandler {
async fn handle_effect(
&self,
effect: &Effect,
args: &[Value],
_context: &EffectContext,
) -> Result<MonadicComputation<Value>> {
match effect {
Effect::IO => {
if let Some(Value::Symbol(op_symbol)) = args.first() {
match format!("symbol_{id}", id = op_symbol.id()).as_str() {
"write" | "display" => {
if let Some(value) = args.get(1) {
let value_cloned = value.clone();
let io_comp: IO<Value> = IO::<()>::write(value_cloned.clone()).map(move |_| value_cloned.clone());
Ok(MonadicComputation::IO(io_comp))
} else {
Ok(MonadicComputation::Pure(Value::Unspecified))
}
}
"read-line" => {
let io_comp: IO<Value> = IO::<String>::read_line().map(Value::string);
Ok(MonadicComputation::IO(io_comp))
}
"open-input-file" => {
if let Some(Value::Literal(crate::ast::Literal::String(filename))) = args.get(1) {
let io_comp: IO<Value> = IO::<String>::pure(format!("opened: {filename}"))
.map(|_| Value::Unspecified); Ok(MonadicComputation::IO(io_comp))
} else {
Err(Box::new(Error::runtime_error(
"open-input-file requires filename argument".to_string(),
None,
)))
}
}
_ => Ok(MonadicComputation::Pure(Value::Unspecified)),
}
} else {
Ok(MonadicComputation::Pure(Value::Unspecified))
}
}
_ => Err(Box::new(Error::runtime_error(
format!("IO handler cannot handle effect: {effect:?}"),
None,
)))
}
}
fn effect_name(&self) -> &str {
"io"
}
fn can_handle(&self, effect: &Effect) -> bool {
matches!(effect, Effect::IO)
}
fn priority(&self) -> i32 {
100 }
fn metadata(&self) -> EffectHandlerMetadata {
EffectHandlerMetadata {
name: "MonadicIOHandler".to_string(),
version: "1.0.0".to_string(),
description: "Handler for IO effects in monadic evaluator".to_string(),
supported_effects: vec![Effect::IO],
capabilities: vec![
EffectCapability::AsyncSupport,
EffectCapability::Streaming,
],
}
}
}
impl MonadicStateHandler {
pub fn new() -> Self {
Self {
state: Arc::new(Mutex::new(HashMap::new())),
config: StateHandlerConfiguration::default(),
}
}
}
#[async_trait]
impl UnifiedEffectHandler for MonadicStateHandler {
async fn handle_effect(
&self,
effect: &Effect,
args: &[Value],
_context: &EffectContext,
) -> Result<MonadicComputation<Value>> {
match effect {
Effect::State => {
if let Some(Value::Symbol(op_symbol)) = args.first() {
match format!("symbol_{id}", id = op_symbol.id()).as_str() {
"get" => {
let state = self.state.lock().unwrap();
let state_map = state.clone();
let state_value = Value::from_hash_map(state_map);
Ok(MonadicComputation::Pure(state_value))
}
"put" => {
if let Some(new_state) = args.get(1) {
let mut state = self.state.lock().unwrap();
state.clear();
state.insert("value".to_string(), new_state.clone());
Ok(MonadicComputation::Pure(Value::Unspecified))
} else {
Err(Box::new(Error::runtime_error(
"put requires state argument".to_string(),
None,
)))
}
}
_ => Ok(MonadicComputation::Pure(Value::Unspecified)),
}
} else {
Ok(MonadicComputation::Pure(Value::Unspecified))
}
}
_ => Err(Box::new(Error::runtime_error(
format!("State handler cannot handle effect: {effect:?}"),
None,
)))
}
}
fn effect_name(&self) -> &str {
"state"
}
fn can_handle(&self, effect: &Effect) -> bool {
matches!(effect, Effect::State)
}
fn priority(&self) -> i32 {
90 }
}
impl MonadicErrorHandler {
pub fn new() -> Self {
Self {
config: ErrorHandlerConfiguration::default(),
}
}
}
#[async_trait]
impl UnifiedEffectHandler for MonadicErrorHandler {
async fn handle_effect(
&self,
effect: &Effect,
args: &[Value],
_context: &EffectContext,
) -> Result<MonadicComputation<Value>> {
match effect {
Effect::Error => {
if let Some(Value::Symbol(op_symbol)) = args.first() {
match format!("symbol_{id}", id = op_symbol.id()).as_str() {
"throw" | "error" => {
if let Some(error_value) = args.get(1) {
let error_msg = format!("{error_value}");
let error = Error::runtime_error(error_msg, None);
Ok(MonadicComputation::Either(Either::left(error)))
} else {
let error = Error::runtime_error("Unspecified error".to_string(), None);
Ok(MonadicComputation::Either(Either::left(error)))
}
}
"try" => {
if let Some(computation_value) = args.get(1) {
Ok(MonadicComputation::Either(Either::right(computation_value.clone())))
} else {
Ok(MonadicComputation::Either(Either::right(Value::Unspecified)))
}
}
_ => Ok(MonadicComputation::Pure(Value::Unspecified)),
}
} else {
Ok(MonadicComputation::Pure(Value::Unspecified))
}
}
_ => Err(Box::new(Error::runtime_error(
format!("Error handler cannot handle effect: {effect:?}"),
None,
)))
}
}
fn effect_name(&self) -> &str {
"error"
}
fn can_handle(&self, effect: &Effect) -> bool {
matches!(effect, Effect::Error)
}
fn priority(&self) -> i32 {
80 }
}
impl MonadicMaybeHandler {
pub fn new() -> Self {
Self {
config: MaybeHandlerConfiguration::default(),
}
}
}
#[async_trait]
impl UnifiedEffectHandler for MonadicMaybeHandler {
async fn handle_effect(
&self,
effect: &Effect,
args: &[Value],
_context: &EffectContext,
) -> Result<MonadicComputation<Value>> {
if let Some(Value::Symbol(op_symbol)) = args.first() {
match format!("symbol_{id}", id = op_symbol.id()).as_str() {
"just" | "some" => {
if let Some(value) = args.get(1) {
Ok(MonadicComputation::Maybe(Maybe::just(value.clone())))
} else {
Ok(MonadicComputation::Maybe(Maybe::nothing()))
}
}
"nothing" | "none" => {
Ok(MonadicComputation::Maybe(Maybe::nothing()))
}
"maybe-bind" => {
if args.len() >= 3 {
let maybe_val = args.get(1).cloned().unwrap_or(Value::Nil);
let func_val = args.get(2).cloned().unwrap_or(Value::Nil);
let maybe = if maybe_val == Value::Nil {
Maybe::nothing()
} else {
Maybe::just(maybe_val)
};
Ok(MonadicComputation::Maybe(maybe))
} else {
Err(Box::new(Error::runtime_error(
"maybe-bind requires maybe and function arguments".to_string(),
None,
)))
}
}
_ => Ok(MonadicComputation::Pure(Value::Unspecified)),
}
} else {
Ok(MonadicComputation::Pure(Value::Unspecified))
}
}
fn effect_name(&self) -> &str {
"maybe"
}
fn can_handle(&self, effect: &Effect) -> bool {
match effect {
Effect::Custom(name) => name == "maybe",
_ => false,
}
}
fn priority(&self) -> i32 {
70 }
}
impl Default for EffectRegistryConfiguration {
fn default() -> Self {
Self {
enable_effect_chaining: true,
max_chain_depth: 100,
enable_parallel_effects: false,
effect_timeout_ms: 5000,
}
}
}
impl Default for IOHandlerConfiguration {
fn default() -> Self {
Self {
enable_file_operations: true,
enable_network_operations: false, default_buffer_size: 8192,
io_timeout_ms: 1000,
}
}
}
impl Default for StateHandlerConfiguration {
fn default() -> Self {
Self {
enable_transactions: false,
max_state_keys: 10000,
enable_persistence: false,
}
}
}
impl Default for ErrorHandlerConfiguration {
fn default() -> Self {
Self {
capture_stack_traces: true,
enable_error_recovery: false,
max_error_chain_depth: 10,
}
}
}
impl Default for MaybeHandlerConfiguration {
fn default() -> Self {
Self {
nothing_behavior: NothingBehavior::ReturnNothing,
}
}
}
static NEXT_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
impl Value {
fn from_hash_map(map: HashMap<String, Value>) -> Value {
if map.is_empty() {
Value::Nil
} else {
map.into_iter().next().map(|(_, v)| v).unwrap_or(Value::Nil)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unified_effect_registry() {
let registry = UnifiedEffectRegistry::with_standard_handlers();
let handlers = registry.list_handlers();
assert!(handlers.contains(&"io".to_string()));
assert!(handlers.contains(&"state".to_string()));
assert!(handlers.contains(&"error".to_string()));
assert!(handlers.contains(&"maybe".to_string()));
}
#[cfg(feature = "async-runtime")]
#[tokio::test]
async fn test_io_handler() {
let handler = MonadicIOHandler::new();
let effect = Effect::IO;
let args = vec![
Value::symbol_from_str("write".to_string()),
Value::string("Hello, World!".to_string()),
];
let context = EffectContext::new();
let result = handler.handle_effect(&effect, &args, &context).await;
assert!(result.is_ok());
match result.unwrap() {
MonadicComputation::IO(_) => {}, _ => panic!("Expected IO computation"),
}
}
#[cfg(feature = "async-runtime")]
#[tokio::test]
async fn test_state_handler() {
let handler = MonadicStateHandler::new();
let effect = Effect::State;
let args = vec![
Value::symbol_from_str("get".to_string()),
];
let context = EffectContext::new();
let result = handler.handle_effect(&effect, &args, &context).await;
assert!(result.is_ok());
match result.unwrap() {
MonadicComputation::Pure(_) => {}, _ => panic!("Expected pure computation"),
}
}
}