#macro #functional #partial-application

nightly macro partial-application-rs

将函数转换为部分应用结构的宏

1个不稳定版本

0.1.0 2020年5月22日

#1091 in 进程宏

MIT/Apache

24KB
456 代码行

使函数部分应用的宏

示例

// Simple function
fn add(x: u32, y: u32) -> i64 {
	(x + y) as i64
}

fn main() {
	println!("add: {}",add(1,2));
}

想法是定义一个宏,将一个函数转换成一个支持部分应用的结构的宏。这会看起来像这样

#[part_app]
fn add(x: u32, y: u32) -> i64 {
	(x + y) as i64
}

fn main() {
	let a = add();
	let two = a.x(|| 2);
    let number = two.y(|| 40);
    assert_eq!(number.call(), 42);
}

#[part_app]会展开成类似这样(为了简洁进行了编辑)。

struct add___Added;

struct add___Empty;

struct __PartialApplication__add_<x, x___FN, y, y___FN, BODYFN>
where
    xFN: FnOnce() -> u32,
    yFN: FnOnce() -> u32,
    BODYFN: FnOnce(u32, u32) -> i64,
{
    xm: ::std::marker::PhantomData<x>,
    ym: ::std::marker::PhantomData<y>,
    x: Option<xFN>,
    y: Option<yFN>,
    body: BODYFN,
}

fn add<x, y>(
) -> __PartialApplication__add_<addEmpty, x, addEmpty, y, impl FnOnce(u32, u32) -> i64>
where
    x: FnOnce() -> u32,
    y: FnOnce() -> u32,
{
    __PartialApplication__add_ {
        x: None,
        y: None,
        xm: ::std::marker::PhantomData,
        ym: ::std::marker::PhantomData,
        body: |x, y| (x + y) as i64,
    }
}

impl<xFN: FnOnce() -> u32, yFN: FnOnce() -> u32, BODYFN: FnOnce(u32, u32) -> i64, y>
    __PartialApplication__add_<addEmpty, xFN, y, yFN, BODYFN>
{
    fn x(
        mut self,
        x: xFN,
    ) -> __PartialApplication__add_<addAdded, xFN, y, yFN, BODYFN> {
        self.x = Some(x);
        unsafe {
            ::std::mem::transmute_copy::<
                __PartialApplication__add_<addEmpty, xFN, y, yFN, BODYFN>,
                __PartialApplication__add_<addAdded, xFN, y, yFN, BODYFN>,
            >(&self)
        }
    }
}

impl<xFN: FnOnce() -> u32, yFN: FnOnce() -> u32, BODYFN: FnOnce(u32, u32) -> i64, x>
    __PartialApplication__add_<x, xFN, addEmpty, yFN, BODYFN>
{
    fn y(
        mut self,
        y: yFN,
    ) -> __PartialApplication__add_<x, xFN, addAdded, yFN, BODYFN> {
        self.y = Some(y);
        unsafe {
            ::std::mem::transmute_copy::<
                __PartialApplication__add_<x, xFN, addEmpty, yFN, BODYFN>,
                __PartialApplication__add_<x, xFN, addAdded, yFN, BODYFN>,
            >(&self)
        }
    }
}

impl<xFN: FnOnce() -> u32, yFN: FnOnce() -> u32, BODYFN: FnOnce(u32, u32) -> i64>
    __PartialApplication__add_<addAdded, xFN, addAdded, yFN, BODYFN>
{
    fn call(self) -> i64 {
        (self.body)(self.x.unwrap()(), self.y.unwrap()())
    }
}

重要的是,这将是一个零成本抽象。理论上,Option将被移除,因为它们没有进行检查。FnOnce可以被优化掉(因为它只是编译器操作语法树),所以结构体不持有任何不可优化的数据。这意味着它的大小应该是0,因此它将被优化掉。

它的工作原理

宏创建了一个函数,该函数生成一个类似于构建器模式的结构体。结构体由已定义的变量进行参数化。只有当变量未定义时,才会为结构体实现变量的定义。最终的调用仅当每个变量都已定义时才会定义。当其位置参数为Added类型时,变量被标记为已定义。当其位置由Empty类型参数化时,它被标记为未定义。

限制

为了尽可能优化,我避免了任何堆分配。这阻止了我抽象闭包的类型。任何PartialApplication结构体的实例只能持有一种类型的闭包。这也阻止了复制。为了解决这个问题,添加了poly属性以启用堆分配,从而使具有相同特质的所有闭包都同样可接受。属性Clone允许在调用之前克隆部分构造的函数。value允许传入值而不是结构体。

依赖项

~1.5MB
~36K SLoC