2 个不稳定版本

0.2.0 2020 年 4 月 12 日
0.1.0 2020 年 4 月 4 日

#5 in #first-order

MIT 许可证

3KB

Defunctionalize

Rust 的 proc-macro 去函数化。

注意:这还处于非常初级的阶段,我刚刚开始研究其机制。

去函数化是一种消除高阶函数,并用一阶函数代替的技术。

虽然这会降低一些灵活性,但也有一些好处。特别是,它允许序列化行为。

功能

  • 将模块中的所有公共函数去函数化到一个枚举中。
  • 方法中的额外参数将被移动到枚举中。
  • 可以对模块应用 derive 来为生成的枚举推导。

用法

  1. defunctionalize 属性应用于模块。此属性接受一个函数签名,即您打算去函数化的函数签名。此签名需要命名参数,并支持泛型。

    通常,生成的枚举类型的名称是从模块的名称计算得出的,但通过在此签名中添加名称,将使用该名称。请注意,在这种情况下,名称不会自动转换为驼峰式。

    // Basic usage
    #[defunctionalize(fn(lhs: usize, rhs: usize) -> usize)]
    mod defunc_a {}
    
    // Generics are supported
    #[defunctionalize(fn<T: std::ops::Add>(lhs: T, rhs: T) -> T::Output)]
    mod defunc_b {}
    
    // The function name will override the module name
    #[defunctionalize(fn DefuncC(lhs: usize, rhs: usize) -> usize)]
    mod hello {} // The generated item will be `enum DefuncC { ... }`
    
    // Where clauses are supported too
    #[defunctionalize(fn<T>(lhs: T, rhs: T) -> T::Output where T: Add)]
    mod defunc_d {}
    
  2. 您还可以将 derive 属性应用于此模块。语法与常规语法相同,并且将在生成的枚举上推导特质。将对枚举情况字段的类型应用常规限制。

  3. 在此模块中定义 pub 函数。它们将被转换为枚举情况。非 pub 函数可以定义为辅助函数,但不会被添加为枚举情况。

    这些函数必须至少具有在 defunctionalize 属性中定义的签名,但也可以有在列出的参数之前额外的参数。返回类型必须匹配。

    函数的名称将转换为驼峰式以成为枚举情况的名字。

    #[defunctionalize(fn(lhs: usize, rhs: usize) -> usize)]
    mod defunc_a {
        // Helper won't get defunctionalized
        fn helper(s: String) -> usize { s.len() }
    
        // This signature matches
        pub fn add(lhs: usize, rhs: usize) -> usize { lhs + rhs }
    
        // Extra parameters at the front get converted to enum fields
        pub fn add_plus_n(n: usize, lhs: usize, rhs: usize) -> usize { lhs + rhs }
    
        // This is not supported: the signature is wrong
        pub fn bad(x: String, y: usize) -> &'static str { "what" }
    }
    

示例

最基本的使用方法如下

use defunctionalize::defunctionalize;

// Apply the defunctionalize attribute to a module. This attribute takes one parameter
// which is the signature of the generated `call` method.
//
// In this example, we are doing a mathematical operation, so our defunctionalized
// function takes two integers, and returns the result of the operation.
#[defunctionalize(fn(x: u32, y: u32) -> u32)]
pub mod operation {
    // All public functions in this module are then defunctionalized. Private functions
    // do not get defunctionalized, but they may be used internally.
    pub fn add(x: u32, y: u32) -> u32 { x + y }
    pub fn sub(x: u32, y: u32) -> u32 { x - y }
    pub fn mult(x: u32, y: u32) -> u32 { x * y }
    pub fn div(x: u32, y: u32) -> u32 { x / y }
    pub fn rem(x: u32, y: u32) -> u32 { x % y }
}

// The above module will be compiled into an enum, with a case for each
// public function:
//
// pub enum Operation {
//     Add,
//     Sub,
//     Mult,
//     Div,
//     Rem,
// }
//
// The name of the module is converted to CamelCase to become the name of the enum.
// Similarly, the name of the functions are converted to CamelCase to become the case
// names

// Values of the resulting enum can then be passed to functions. They contain a `call`
// method, which uses the signature provided in the defunctionalize attribute, which
// calls the corresponding method
fn perform_operation(operation: Operation) -> u32 {
    operation.call(6, 7)
}

// And here we use the defunctionalized module to compute 6 * 7 = 42!
fn main() {
    assert_eq!(42, perform_operation(Operation::Mult));
}

当然,这并不是这项技术的全部功能!在这里,我们将在此基础上扩展,以演示两个更有用的功能

use defunctionalize::defunctionalize;

// First, notice that the function signature here has had one argument removed!
#[defunctionalize(fn(rhs: u32) -> u32)]
// Next, we put some derives *on the module*. These will get moved to the enum. This
// is most useful for serializing your defunctionalized functions.
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub mod operation {
    // Back to the first point now, notice that these functions have not changed! Where
    // do they get the extra parameter though?
    pub fn add(x: u32, y: u32) -> u32 { x + y }
    pub fn sub(x: u32, y: u32) -> u32 { x - y }
    pub fn mult(x: u32, y: u32) -> u32 { x * y }
    pub fn div(x: u32, y: u32) -> u32 { x / y }
    pub fn rem(x: u32, y: u32) -> u32 { x % y }
}

// The extra parameter got moved to the enum! Now we can essentially serialize an
// entire function call.
//
// #[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
// pub enum Operation {
//     Add(u32),
//     Sub(u32),
//     Mult(u32),
//     Div(u32),
//     Rem(u32),
// }

// Now we only need to provide one parameter to the `call` method:
fn perform_operation(operation: Operation) -> u32 {
    operation.call(7)
}

// And here we use the defunctionalized module to compute 49 - 7 = 42!
fn main() {
    assert_eq!(42, perform_operation(Operation::Sub(49)));
}

依赖项

~0–315KB