4 个版本
0.1.3 | 2021 年 1 月 8 日 |
---|---|
0.1.2 | 2021 年 1 月 6 日 |
0.1.1 | 2021 年 1 月 6 日 |
0.1.0 | 2021 年 1 月 6 日 |
#495 在 数据结构
228 每月下载量
在 9 个 crate(5 个直接使用)中使用
13KB
160 行
do-notation
,将单调 do
语法带到 Rust。
此 crate 提供了 m!
宏,它提供了 Haskell 单调语法糖 do
。
注意:不能使用
do!
语法,因为do
是 Rust 的保留关键字。
语法与 Haskell 中的非常相似
- 您使用
m!
宏;在 Haskell 中,您使用do
关键字。 <-
语法糖通过使用闭包 进入 右侧将左侧绑定到单调右侧。- 像 Rust 中几乎任何语句一样,您必须以分号(
;
)结束您的语句。 - 最后一行必须不包含分号(
;
)或包含return
关键字。 - 您只能在最后一行使用
return
。 - 只包含一个表达式和分号的行是有效的语句,其效果与
_ <- expr
相同。 let
绑定以let <pattern> = <expr>;
的形式允许,并具有常规 Rust 的意义。
如何让我的单调与 m!
一起工作?
因为单调是高阶类型,所以无法以完全类型系统优雅的方式定义单调 do 语法。然而,此 crate 基于 Haskell 中的可重新绑定概念(即您可以更改 >>=
操作符的类型),因此 m!
有一个类型系统要求和一个语法要求。
首先,您需要实现一个特性:Lift
,它允许将值 A
提升 到 A
的 单调结构。例如,将 A
提升到 Option
单调结构将得到 Option<A>
。
然后,您需要提供一个 and_then
方法,它与 Haskell 的 >>=
操作符类似。选择使用 and_then
而不是像 flat_map
或 bind
这样的正确名称,是由于标准库的当前状态——像 Option
和 Result<_, E>
这样的单调没有定义 flat_map
,但是有 and_then
。类型签名没有被强制执行,但
and_then
必须是一个二进制函数,它接受类型A
、一个闭包A -> Monad<B>
并返回Monad<B>
,其中Monad
是您为and_then
添加的单调。例如,如果您为Option
实现,则and_then
接受一个A
,一个闭包A -> Option<B>
并返回一个Option<B>
。and_then
必须移动它的第一个参数,它必须是self
。类型Self
没有被强制执行。and_then
的闭包必须接受一个具有FnOnce
闭包的A
。
<-
操作符的含义
<-
语法糖实际上不是一个操作符:它不是纯 Rust。相反,它是在 m!
中定义的一个技巧,允许同时使用 Lift::lift
和 and_then
。当您查看 do-notation 块内的代码时,每个单调语句(在这个软件包中用 ;
分隔)可以想象为闭包中的一个新级别——确实是传递给 and_then
的闭包。
第一个例子:有错误的代码
人们最初学习的第一个单调应用之一是 有错误的 影响——Haskell 中的 Maybe
。在 Rust
中,它是 Option
。 Option
是一个有趣的单调,因为它允许您提前失败。
use do_notation::m;
let r = m! {
x <- Some("Hello, world!");
y <- Some(3);
Some(x.len() * y)
};
assert_eq!(r, Some(39));
binding <- expr
语法解开右侧部分并将其绑定到 binding
,使其在后续调用中可用——记住,嵌套闭包。最后一行显式地重新进入结构(在这里,Option
)。
请注意,可以在不指定结构如何或知道结构的情况下重新进入结构(使用 Option
,你可以使用 Some
重新进入)。你可以使用 return
关键字,它将自动将值提升到正确的结构。
use do_notation::m;
let r = m! {
x <- Some(1);
y <- Some(2);
z <- Some(3);
return [x, y, z];
};
assert_eq!(r, Some([1, 2, 3]));