1 个稳定版本
1.0.0 | 2024年1月6日 |
---|
#796 in 过程宏
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
方法的类型一起工作。如果您发现有任何例子表明这不起作用,请告诉我!
这与?
运算符有什么不同?
是的!据我所知,目前?
运算符只与Option
和Result
类型一起工作。存在一个实验性的Try
特质,可以用来重载?
运算符,但它提供的语义与这个crate中的!
符号不同。与?
不同,!
符号适用于任何提供适合and_then
接口的任意类型。例如,您可能想看看使用自定义编写的List
monad的list001
测试。
值是如何绑定的顺序?
带有!
标记的表达式按照它们在源代码中编写的顺序从左到右绑定。在嵌套带有!
标记的表达式中,先绑定内部表达式。然后,它们在外部表达式中用新的名称替换,然后绑定外部表达式。
依赖关系
~275–720KB
~17K SLoC