murarth / ketos

Lisp dialect scripting and extension language for Rust programs
Apache License 2.0
751 stars 45 forks source link

Access to context inside rust bindings. #34

Closed Rafagd closed 6 years ago

Rafagd commented 6 years ago

So I have this function in rust bound to ketos that receives a lambda as an argument and calls the lambda in a loop with a different value for its arguments on every run.

Sounds simple enough, but my function can't receive the context as a parameter, so I'm stuck using the interpreter as a global variable.

I understand that functional languages are not supposed to care about the context, but all functions I've found to execute the lambda require me to send the context as a parameter.

What should I do?

murarth commented 6 years ago

If this Rust function is being called from Ketos code, it would be best to use the same context for subsequent executions. Perhaps a Rust closure could carry a reference to or a clone of the context.

However, if this isn't possible, you can create a new execution context (using a Scope, which can be extracted from the Lambda struct's weak reference).

Rafagd commented 6 years ago

Ok, so here is a minimal example of what I'm doing. I would love to get rid of INTERP, even if that means ketos_fn! adding the scope as the first argument.

#[macro_use]
extern crate ketos;

static mut INTERP: *mut ketos::Interpreter = { 0 as *mut _ };

fn repeat(n: usize, lambda: &ketos::Value) -> Result<(), ketos::Error>
{
    for i in 0..n {
        let _ = unsafe { (*INTERP).call_value(lambda.clone(), vec![ i.into() ])? };
    }

    Ok(())
}

fn main ()
{
    let mut interp = ketos::Interpreter::new();

    unsafe {
        INTERP = &mut interp as *mut _
    };

    ketos_fn! {
        interp.scope() => "rp" =>
            fn repeat(n: usize, lambda: &ketos::Value) -> ()
    }

    interp.run_code("(rp 1000 (lambda (i) (println \"~a\" i)))", None).unwrap();
}
murarth commented 6 years ago

One alternative would be, rather than using ketos_fn! to create a callable Ketos value, to use Value::new_foreign_fn to wrap a function with the signature fn(&Context, &mut [Value]) -> Result<(), Error>.

You can then use the macro ketos_args! to get (usize, &Value) from the argument slice and execute the given value using the passed &Context.