swan_core/value/func.rs
1use super::*;
2
3
4
5
6// ----------------------------------------------------------------------------
7// BuiltinFn (a native Rust function callable from Swan)
8// ----------------------------------------------------------------------------
9
10/// A native Rust function that can be called from Swan code.
11///
12/// # Why a newtype instead of a bare `Rc<dyn Fn>`?
13///
14/// 1. **Debug**: `dyn Fn` is not `Debug`; storing a `name` gives meaningful
15/// output in error messages and test output.
16/// 2. **Metadata**: the `name` can be used when building `Undefined` error
17/// values ("function `math.sin` called on a Bool").
18/// 3. **Ergonomics**: `BuiltinFn::new("name", |arg| ...)` is self-documenting.
19///
20/// # Argument convention
21///
22/// The function receives the evaluated right-hand side as a single `Value`.
23/// Multi-argument calls arrive as `Value::Tuple`. The function is responsible
24/// for destructuring the tuple when it expects more than one argument.
25///
26/// # Return value
27///
28/// The function must never panic. Return a [`Value::Undefined`] (for example
29/// with [`Value::undef_unary`]) for any input that is invalid.
30///
31/// # Examples
32///
33/// ```
34/// use swan_core::value::{BuiltinFn, Value};
35///
36/// let negate = BuiltinFn::new("negate", |arg| match arg {
37/// Value::Numb(n) => Value::Numb(-n),
38/// other => Value::undef_unary("negate", other, 0),
39/// });
40/// assert_eq!(negate.call(Value::Numb(2.0)), Value::Numb(-2.0));
41/// ```
42#[derive(Clone)]
43pub struct BuiltinFn {
44 /// Human-readable name, used in Debug output and error messages.
45 pub name: &'static str,
46 func: Rc<dyn Fn(Value) -> Value>,
47}
48
49impl BuiltinFn {
50 /// Create a new built-in function from any `Fn(Value) -> Value`.
51 ///
52 /// Both plain function pointers (`fn(Value) -> Value`) and closures are
53 /// accepted because both coerce to `dyn Fn(Value) -> Value`.
54 pub fn new(name: &'static str, f: impl Fn(Value) -> Value + 'static) -> Self {
55 Self { name, func: Rc::new(f) }
56 }
57
58 /// Invoke the built-in with the given argument.
59 #[inline]
60 pub fn call(&self, arg: Value) -> Value {
61 (self.func)(arg)
62 }
63}
64
65impl std::fmt::Debug for BuiltinFn {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 write!(f, "<builtin {}>", self.name)
68 }
69}
70
71