Expand description
First-class callable CEL values with declared signatures. A first-class, callable CEL value: a compiled body plus its declared signature.
See docs/superpowers/specs/2026-08-21-cel-closures-design.md for the full design rationale
(why Rc, why RefCell, why no captured environment).
§Examples
Build a closure that adds two i32 values, then call it with different arguments:
use cel_runtime::{DynClosure, DynSegment};
use std::any::TypeId;
// Compile a body that takes two i32 arguments and returns their sum.
let mut body = DynSegment::new::<()>();
body.push_arg::<i32>(0);
body.push_arg::<i32>(1);
body.op2(|a: i32, b: i32| a + b).unwrap();
// Wrap it as a callable closure with its declared signature.
let closure = DynClosure::new(
vec![TypeId::of::<i32>(), TypeId::of::<i32>()],
TypeId::of::<i32>(),
body,
);
// Call the closure multiple times with different arguments.
let (a, b) = (2i32, 3i32);
assert_eq!(closure.call::<i32>(&[&a, &b]).unwrap(), 5);
let (c, d) = (10i32, 20i32);
assert_eq!(closure.call::<i32>(&[&c, &d]).unwrap(), 30);Structs§
- DynClosure
- A first-class, callable CEL value: a compiled body plus its declared parameter/return types.
Type Aliases§
- Call
DynFn - A monomorphized dispatcher for
DynClosure::call_boxed: runs a closure’s body viaDynSegment::call_dyn::<R>for some concreteRthe dispatcher itself already knows, boxing the result so the caller (which only knowsRdynamically, as aTypeId) doesn’t need to.