Closed clarkmcc closed 1 year ago
Explored the idea of generic type resolvers with const generics but they're not going to be compatible with more advanced resolvers that require more parameters. They're probably not that much easier to read anyways.
fn add(ftx: FunctionContext) -> ResolveResult {
let a = ftx.resolve::<Argument<0>>()?;
let b = ftx.resolve::<Argument<1>>()?;
Ok(a + b)
}
// instead of
fn add(ftx: FunctionContext) -> ResolveResult {
let a = ftx.resolve(Argument(0))?;
let b = ftx.resolve(Argument(1))?;
Ok(a + b)
}
Now that I think about it, I kind of want to just cut out the middle man and support Axum-style functions. Here's the foundations coming together
fn greet(name: Rc<String>) -> String {
format!("Hello, {}!", name)
}
trait Callable<T> {
fn call(&self, ftx: FunctionContext) -> Result<Value>;
}
trait FromValue {
fn from(expr: &Value) -> Result<Self>
where
Self: Sized;
}
trait IntoResolveResult {
fn into_resolve_result(self) -> ResolveResult;
}
impl<F, T1, T2, T3> Callable<(T1, T2)> for F
where
F: Fn(T1, T2) -> T3 + 'static,
T1: FromValue,
T2: FromValue,
T3: IntoResolveResult,
{
fn call(&self, ftx: FunctionContext) -> ResolveResult {
let arg1 = ftx.resolve(Argument(0))?;
let arg2 = ftx.resolve(Argument(1))?;
let t1 = T1::from(&arg1)?;
let t2 = T2::from(&arg2)?;
self(t1, t2).into_resolve_result()
}
}
Can't figure out a way to store the Callable
s somewhere and call them later... gonna give my mind a break
Okay, making progress. So we can support a much more ergonomic function syntax now. The following functions can be registered and are supported
// Primitive arguments
fn add(a: i32, b: i32) -> i32 {
a + b
}
// Falible operations
fn try() -> Result<Value> {
// maybe fail
}
// Methods on types
fn starts_with(Target(target): Target<Rc<String>>, prefix: Rc<String>) -> bool {
target.starts_with(prefix.as_str())
}
Tests are working. I want to benchmark a few cloning compromises I had to make after I beat my head against lifetimes this week.
Benchmarks on function calls regressed 10-30% but are still generally in the nanosecond range which is far better performance then cel-go. criterion.zip
Originally this issue started with utilities for making it easier to write functions by providing function resolvers (shown below) but eventually evolved into Axum-style magic function parameters.
Function value resolvers
Instead of
Resolvers allow us to do things like
and hopefully a few more other handy things all from the same
resolve
function as this PR goes along.The previous example was for single expression resolution. You can also resolve multiple. Instead of
we can do this using the same resolve function.
Target value resolvers
Previously you had to do
to unpack the target. Now you can ask for specific types by
or just this if you need the old behavior.