Module crossmist::fns

source ·
Expand description

Utilities for passing function callbacks between processes.

It is common to use callbacks to specialize function behavior. Capturing lambdas play an especially big role in this. They are, however, of complex opaque types that cannot be inspected. Therefore, passing lambdas is not just complicated because they would have to be of type dyn Object + Fn() -> (), which Rust does not support at the moment, but downright impossible in case of captures.

To fix the following code:

use crossmist::{func, main, Object};

#[main]
fn main() {
    let x = 7;
    println!("{}", go.run(5, Box::new(|y| x + y)).unwrap());
}

#[func]
fn go(x: i32, f: Box<dyn Object + Fn(i32) -> i32>) -> i32 {
    f(x)
}

…we have to use a macro, and also a different invocation syntax:

use crossmist::{FnObject, func, lambda, main};

#[main]
fn main() {
    let x = 7;
    println!("{}", go.run(5, lambda! { move(x: i32) |y: i32| -> i32 { x + y } }).unwrap());
}

#[func]
fn go(x: i32, f: Box<dyn FnObject<(i32,), Output = i32>>) -> i32 {
    f.call_object((x,))
}

The macro syntax is somewhat similar to that of capturing lambdas. call_object is similar to std::ops::Fn::call. If you’re using nightly Rust, you can directly do f(x) if you opt in by enabling the nightly feature.

Another complication is when the callback should capture a non-copyable value (e.g. Box) and then be called multiple times. This cannot be detected automatically, so slightly different syntax is used:

use crossmist::{FnObject, func, lambda, main};

#[main]
fn main() {
    let x = Box::new(7);
    println!("{}", go.run(5, lambda! { move(&x: &Box<i32>) |y: i32| -> i32 { **x + y } }).unwrap());
}

#[func]
fn go(x: i32, f: Box<dyn FnObject<(i32,), Output = i32>>) -> i32 {
    f.call_object((x,))
}

Similarly, &mut x can be used if the object is to be modified. Note that this still moves x into the lambda.

Under the hood, the macro uses currying, replacing |y| x + y with |x, y| x + y with a pre-determined x variable, and makes |x, y| x + y a callable Object by using #[func]:

use crossmist::{BindValue, FnObject, func, main};

#[main]
fn main() {
    #[func]
    fn add(x: i32, y: i32) -> i32 {
        x + y
    }

    let x = 7;
    println!("{}", go.run(5, Box::new(add.bind_value(x))).unwrap());
}

#[func]
fn go(x: i32, f: Box<dyn FnObject<(i32,), Output = i32>>) -> i32 {
    f.call_object((x,))
}

Structs§

  • A wrapper for fn(...) -> ... implementing FnObject.

Traits§

  • A callable object that can be called multiple times and might mutate state.
  • FnObjectnightly
    A callable object that can be called multiple times without mutating state.
  • A callable object that can be called at least once.
  • Metaprogramming on fn(...) -> ... types.
  • Tuplenightly
    A tuple.