简体   繁体   English

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

[英]How to do Higher Order Composition in Rust?

I have this function that works:我有这个有效的 function:

fn compose<A, B, C>(f: impl Fn(A) -> B, g: impl Fn(B) -> C) -> impl Fn(A) -> C {
    move |x: A| g(f(x))
}

I'd like to use currying in this situation, so that I can do:我想在这种情况下使用柯里化,这样我就可以做到:

/* THIS */
compose(f)(g)(x)

/* Instead of this */
compose(f, g)(x)

Is it even reasonable to think about making a macro such that this is possible?考虑制作一个这样的宏是否可行?

compose(f)(g)(h)···(x)

I've managed to get something that's kinda similar to what I want:我设法得到了一些类似于我想要的东西:

fn compose<A, B, C, F: 'static, G: 'static>(f: F) -> impl Fn(G) -> Box<dyn Fn(A) -> C>
where
    F: Fn(A) -> B + Copy,
    G: Fn(B) -> C + Copy,
{
    move |g: G| Box::new(move |x: A| g(f(x)))
}

let inc = |x| x + 1;
let mult2 = |x| x * 2;

let inc_compose = compose(inc);

println!("{}", inc_compose(mult2)(3)); // 8

Now there's a new problem: when creating a higher-order function based on my compose function, I need to give the function a type dependent on another function: Now there's a new problem: when creating a higher-order function based on my compose function, I need to give the function a type dependent on another function:

let inc   = |x: usize| x + 1;
let mult2 = |x: usize| x * 2;
let mult3 = |x: usize| x * 3;

let inc_compose = compose(inc);

println!("{}", inc_compose(mult2)(3)); // 8

println!("{}", inc_compose(mult3)(3)); // ERROR: [rustc E0308] [E] mismatched types

Is there a way to avoid this error?有没有办法避免这个错误?

None of these similar posts answer my question:这些类似的帖子都没有回答我的问题:

The main reason is that I want currying to get the ability to do point-free programming.主要原因是我想通过currying获得做无点编程的能力。

In Rust, currying is hard.在 Rust 中,很难进行柯里化。 The strict type system, lifetimes, and nested impl traits not existing all conspire to ruin your day when you try.严格的类型系统、生命周期和不存在的嵌套impl特征都会在您尝试时破坏您的一天。 However, it is possible to create a macro to curry functions, and I did so for code golf at one point.但是,可以创建一个宏来 curry 函数,我曾经为代码高尔夫这样做过。 The ungolfed version is here:未打高尔夫球的版本在这里:

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

This matches on expressions of the form name(arg1, type1)(arg2, type2)...(argN, typeN) and returns a chain of move closures leading to the function call.这匹配name(arg1, type1)(arg2, type2)...(argN, typeN)形式的表达式,并返回导致 function 调用的move闭包链。 Most of the time, however, you can just use _ for the type and let inference figure it out.但是,大多数情况下,您可以只使用_作为类型并让推理来解决。 Using it with compose is simple:将它与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);

The code generated by the macro, in this case, looks like this:在这种情况下,宏生成的代码如下所示:

move |f: _| move |g: _| compose(f, g)

It simply takes the supplied name and type and pastes it into the closure heads, so you can use it to force arguments for generic functions into concrete types.它只需将提供的名称和类型粘贴到闭包头中,因此您可以使用它来强制 arguments 将通用函数转换为具体类型。 Playground 操场

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM