1 个稳定版本

1.0.0 2024年1月6日

#796 in 过程宏

Apache-2.0

9KB
51

!-notation, brought to Rust

此 crate 提供了 bang! 宏,类似于 !-notation in Idris2。使用它,以下表达式

bang!(!x + !y + z)

会被转换成

x.and_then(|x| {
    y.and_then(|y| {
        x + y + z
    })
})

这解决了什么问题?

Rust 提供了整洁的容器,使得错误处理变得简单方便。然而,有时,像我这样的人可能会遇到一个特殊的情况。

假设有一个函数可以生成一个 Option

fn some_func(x1: T1, x2: T2, x3: T3) -> Option<u32>

而且你甚至已经准备好调用该函数的参数。但是,有一个问题:参数也是以某种方式生成的,使得它们被包裹在 Option 中。所以,我们手中只有 x1: Option<T1>x2: Option<T2>x3: Option<T3>。这当然会促使我们编写一些类似的东西

x1.and_then(|x1| {
    x2.and_then(|x2| {
        x3.and_then(|x3| {
            some_func(x1, x2, x3)
        })
    })
});

这看起来很糟糕,而且需要我们编写很多无用的符号!幸运的是,编程语言 Idris2(我非常喜欢)提供了一个很好的解决方案。它有一个很好的语法糖,称为 !-notation。在 Idris 中,以 ! 前缀的表达式尽可能提升并绑定到一个新的名称。然后原始表达式被替换为这个新绑定的名称。根据 Idris 文档,它将以下表达式转换成

f !(g !(print y) !x)

变成

do y' <- print y
   x' <- x
   g' <- g y' x'
   f g'

此 crate 提供了一个过程宏,在 Rust 中执行类似的转换。只需导入它

use bang_notation::bang;

然后我们就可以用简洁的表达式替换原来难以阅读的混乱代码

bang!(some_func(!x1, !x2, !x3))

它将被简化为最初我们提出的and_then链。

您可以用它来做什么?

在Idris中,!符号可以与任意monads一起使用。这个Rust版本旨在尽可能通用。它应该与任何提供具有适当签名的and_then方法的类型一起工作。如果您发现有任何例子表明这不起作用,请告诉我!

这与?运算符有什么不同?

是的!据我所知,目前?运算符只与OptionResult类型一起工作。存在一个实验性的Try特质,可以用来重载?运算符,但它提供的语义与这个crate中的!符号不同。与?不同,!符号适用于任何提供适合and_then接口的任意类型。例如,您可能想看看使用自定义编写的List monad的list001测试。

值是如何绑定的顺序?

带有!标记的表达式按照它们在源代码中编写的顺序从左到右绑定。在嵌套带有!标记的表达式中,先绑定内部表达式。然后,它们在外部表达式中用新的名称替换,然后绑定外部表达式。

依赖关系

~275–720KB
~17K SLoC