use std::borrow::Cow; use std::collections::HashMap; use std::future::Future; use std::io; use std::sync::{Arc, Mutex}; use futures::FutureExt; use futures::future::LocalBoxFuture; use itertools::Itertools; use lazy_static::lazy_static; use never::Never; use orchid_api_traits::Encode; use orchid_base::clone; use orchid_base::error::OrcRes; use orchid_base::name::Sym; use trait_set::trait_set; use crate::atom::{Atomic, MethodSet}; use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant}; use crate::conv::ToExpr; use crate::expr::{Expr, ExprHandle}; use crate::system::SysCtx; trait_set! { trait FunCB = Fn(Vec) -> LocalBoxFuture<'static, OrcRes> + Send + Sync + 'static; } pub trait ExprFunc: Clone + Send + Sync + 'static { const ARITY: u8; fn apply(&self, v: Vec) -> impl Future>; } lazy_static! { static ref FUNS: Mutex)>> = Mutex::default(); } /// An Atom representing a partially applied named native function. These /// partial calls are serialized into the name of the native function and the /// argument list. /// /// See [Lambda] for the non-serializable variant #[derive(Clone)] pub(crate) struct Fun { path: Sym, args: Vec, arity: u8, fun: Arc, } impl Fun { pub fn new>(path: Sym, f: F) -> Self { let mut fung = FUNS.lock().unwrap(); let fun = if let Some(x) = fung.get(&path) { x.1.clone() } else { let fun = Arc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local())); fung.insert(path.clone(), (F::ARITY, fun.clone())); fun }; Self { args: vec![], arity: F::ARITY, path, fun } } } impl Atomic for Fun { type Data = (); type Variant = OwnedVariant; fn reg_reqs() -> MethodSet { MethodSet::new() } } impl OwnedAtom for Fun { type Refs = Vec; async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) } async fn call_ref(&self, arg: ExprHandle) -> Expr { let new_args = self.args.iter().cloned().chain([Expr::from_handle(Arc::new(arg))]).collect_vec(); if new_args.len() == self.arity.into() { (self.fun)(new_args).await.to_expr() } else { Self { args: new_args, arity: self.arity, fun: self.fun.clone(), path: self.path.clone() } .to_expr() } } async fn call(self, arg: ExprHandle) -> Expr { self.call_ref(arg).await } async fn serialize(&self, _: SysCtx, sink: &mut (impl io::Write + ?Sized)) -> Self::Refs { self.path.to_api().encode(sink); self.args.clone() } async fn deserialize(ctx: impl DeserializeCtx, args: Self::Refs) -> Self { let path = Sym::from_api(ctx.decode()).await; let (arity, fun) = FUNS.lock().unwrap().get(&path).unwrap().clone(); Self { args, arity, path, fun } } } /// An Atom representing a partially applied native lambda. These are not /// serializable. /// /// See [Fun] for the serializable variant #[derive(Clone)] pub struct Lambda { args: Vec, arity: u8, fun: Arc, } impl Lambda { pub fn new>(f: F) -> Self { let fun = Arc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local())); Self { args: vec![], arity: F::ARITY, fun } } } impl Atomic for Lambda { type Data = (); type Variant = OwnedVariant; fn reg_reqs() -> MethodSet { MethodSet::new() } } impl OwnedAtom for Lambda { type Refs = Never; async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) } async fn call_ref(&self, arg: ExprHandle) -> Expr { let new_args = self.args.iter().cloned().chain([Expr::from_handle(Arc::new(arg))]).collect_vec(); if new_args.len() == self.arity.into() { (self.fun)(new_args).await.to_expr() } else { Self { args: new_args, arity: self.arity, fun: self.fun.clone() }.to_expr() } } async fn call(self, arg: ExprHandle) -> Expr { self.call_ref(arg).await } } mod expr_func_derives { use orchid_base::error::OrcRes; use super::ExprFunc; use crate::conv::{ToExpr, TryFromExpr}; use crate::func_atom::Expr; macro_rules! expr_func_derive { ($arity: tt, $($t:ident),*) => { paste::paste!{ impl< $($t: TryFromExpr, )* Out: ToExpr, Func: Fn($($t,)*) -> Out + Clone + Send + Sync + 'static > ExprFunc<($($t,)*), Out> for Func { const ARITY: u8 = $arity; async fn apply(&self, v: Vec) -> OrcRes { assert_eq!(v.len(), Self::ARITY.into(), "Arity mismatch"); let [$([< $t:lower >],)*] = v.try_into().unwrap_or_else(|_| panic!("Checked above")); Ok(self($($t::try_from_expr([< $t:lower >]).await?,)*).to_expr()) } } } }; } expr_func_derive!(1, A); expr_func_derive!(2, A, B); expr_func_derive!(3, A, B, C); expr_func_derive!(4, A, B, C, D); expr_func_derive!(5, A, B, C, D, E); expr_func_derive!(6, A, B, C, D, E, F); expr_func_derive!(7, A, B, C, D, E, F, G); expr_func_derive!(8, A, B, C, D, E, F, G, H); expr_func_derive!(9, A, B, C, D, E, F, G, H, I); expr_func_derive!(10, A, B, C, D, E, F, G, H, I, J); expr_func_derive!(11, A, B, C, D, E, F, G, H, I, J, K); expr_func_derive!(12, A, B, C, D, E, F, G, H, I, J, K, L); expr_func_derive!(13, A, B, C, D, E, F, G, H, I, J, K, L, M); expr_func_derive!(14, A, B, C, D, E, F, G, H, I, J, K, L, M, N); }