如何在 Rust 中进行高阶组合?

Car*_*mez 7 functional-programming rust

我有这个有效的功能:

\n
fn compose<A, B, C>(f: impl Fn(A) -> B, g: impl Fn(B) -> C) -> impl Fn(A) -> C {\n    move |x: A| g(f(x))\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我想在这种情况下使用柯里化,这样我就可以这样做:

\n
/* THIS */\ncompose(f)(g)(x)\n\n/* Instead of this */\ncompose(f, g)(x)\n
Run Code Online (Sandbox Code Playgroud)\n

考虑制作一个宏来实现这一点是否合理?

\n
compose(f)(g)(h)\xc2\xb7\xc2\xb7\xc2\xb7(x)\n
Run Code Online (Sandbox Code Playgroud)\n

我已经设法得到与我想要的有点相似的东西:

\n
fn compose<A, B, C, F: \'static, G: \'static>(f: F) -> impl Fn(G) -> Box<dyn Fn(A) -> C>\nwhere\n    F: Fn(A) -> B + Copy,\n    G: Fn(B) -> C + Copy,\n{\n    move |g: G| Box::new(move |x: A| g(f(x)))\n}\n\nlet inc = |x| x + 1;\nlet mult2 = |x| x * 2;\n\nlet inc_compose = compose(inc);\n\nprintln!("{}", inc_compose(mult2)(3)); // 8\n
Run Code Online (Sandbox Code Playgroud)\n

现在有一个新问题:当基于我的函数创建高阶函数时compose,我需要为该函数提供依赖于另一个函数的类型:

\n
let inc   = |x: usize| x + 1;\nlet mult2 = |x: usize| x * 2;\nlet mult3 = |x: usize| x * 3;\n\nlet inc_compose = compose(inc);\n\nprintln!("{}", inc_compose(mult2)(3)); // 8\n\nprintln!("{}", inc_compose(mult3)(3)); // ERROR: [rustc E0308] [E] mismatched types\n
Run Code Online (Sandbox Code Playgroud)\n

有没有办法避免这个错误?

\n

这些类似的帖子都没有回答我的问题:

\n\n

主要原因是我希望柯里化能够获得无点编程的能力。

\n

Aid*_*en4 5

在 Rust 中,柯里化是很困难的。当您尝试时,严格的类型系统、生命周期和impl不存在的嵌套特征都会破坏您的一天。然而,可以创建一个宏来柯里化函数,我曾经在代码高尔夫中这样做过。非高尔夫版本在这里:

macro_rules! curry{
    (
        $f:ident
        $(
            ($args:ident : $types:ty)
        )*
    ) => {
        $(move |$args: $types|)*
        $f(
            $($args),*
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

它匹配表单的表达式并返回导致函数调用的闭包name(arg1, type1)(arg2, type2)...(argN, typeN)链。move然而,大多数时候,您可以只使用_类型并让推理来解决它。使用它compose很简单:

let inc = |x| x as u32 + 1;
let double = |x| x * 2;
let curried = curry!(compose(f: _)(g: _));
assert_eq!(curried(inc)(double)(7), 16);
Run Code Online (Sandbox Code Playgroud)

在本例中,宏生成的代码如下所示:

move |f: _| move |g: _| compose(f, g)
Run Code Online (Sandbox Code Playgroud)

它只是获取提供的名称和类型并将其粘贴到闭包头中,因此您可以使用它来将泛型函数的参数强制转换为具体类型。 操场