lambdas/
dsl.rs

1use crate::*;
2
3use std::collections::HashMap;
4use std::fmt::{Debug};
5use std::hash::Hash;
6use std::collections::hash_map::Values;
7
8pub type DSLFn<D> = fn(Env<D>, &Evaluator<D>) -> VResult<D>;
9
10#[derive(Clone)]
11pub struct DSLEntry<D: Domain> {
12    pub name: Symbol, // eg "map" or "0" or "[1,2,3]"
13    pub val: Val<D>,
14    pub tp: Type,
15    pub arity: usize,
16}
17
18#[derive(Clone)]
19pub struct DSL<D:Domain> {
20    pub entries: HashMap<Symbol,DSLEntry<D>>,
21}
22
23impl<D: Domain> DSLEntry<D> {
24    pub fn new(name: Symbol, val: Val<D>, tp: Type) -> Self {
25        let arity = tp.arity();
26        DSLEntry {
27            name,
28            val,
29            tp,
30            arity,
31        }
32    }
33}
34impl<D: Domain> DSL<D> {
35    pub fn new( entries: Vec<DSLEntry<D>> ) -> Self {
36        DSL {
37            entries: entries.into_iter().map(|entry| (entry.name, entry)).collect()
38        }
39    }
40}
41
42
43/// The key trait that defines a domain
44pub trait Domain: Clone + Debug + PartialEq + Eq + Hash + 'static {
45    /// Domain::Data is attached to the Evaluator so all DSL functions will have a
46    /// mut ref to it (through the handle argument). Feel free to make it the empty
47    /// tuple if you dont need it.
48    /// Motivation: For some complicated domains you could leave Ids as pointers and
49    /// have your domaindata be a system to lookup the actual value from the pointer
50    /// (and guarantee no value has multiple pointers so that comparison works by Ids).
51    /// Btw, I briefly implemented it so that the whole system worked by these pointers
52    /// and it was absolutely miserable, see my notes. But this is here if someone finds
53    /// a use for it. Ofc be careful not to break function purity with this but otherwise
54    /// be creative :)
55    type Data: Debug + Default;
56    // type Type: Debug + Clone + PartialEq + Eq + Hash;
57
58    /// given a primitive's symbol return a runtime Val object. For function primitives
59    /// this should return a PrimFun(CurriedFn) object.
60    fn val_of_prim(p: egg::Symbol) -> Option<Val<Self>> {
61        Self::dsl_entry(p).map(|entry| entry.val.clone()).or_else(||
62            Self::val_of_prim_fallback(p))
63    }
64
65    fn val_of_prim_fallback(p: egg::Symbol) -> Option<Val<Self>>;
66
67    /// given a function primitive's symbol return the function pointer
68    /// you can use to call the function.
69    /// Breakdown of the function type: it takes a slice of values as input (the args)
70    /// along with a mut ref to an Expr (I'll refer to as a "handle") which is necessary
71    /// for calling .apply(f,x). This setup with a handle guarantees we can always track
72    /// when applys happen and log them in our Expr.evals, and also it's necessary for
73    /// executing LamClosures in order to look up their body Id (and we wouldn't want
74    /// LamClosures to carry around full Exprs because that would break the Expr.evals tracking)
75    fn lookup_fn_ptr(p: Symbol) -> DSLFn<Self>;
76
77    fn dsl_entry(p: Symbol) -> Option<&'static DSLEntry<Self>>;
78
79    fn dsl_entries() -> Values<'static, Symbol, DSLEntry<Self>>;
80
81    fn type_of_dom_val(&self) -> Type;
82
83    fn type_of_prim(p: Symbol) -> Type {
84        Self::dsl_entry(p).map(|entry| entry.tp.clone()).unwrap_or_else(|| {
85            Self::type_of_dom_val(&Self::val_of_prim(p).unwrap().dom().unwrap())
86        })
87    }
88
89}
90