3 个版本
0.1.2 | 2023年5月19日 |
---|---|
0.1.1 | 2023年5月19日 |
0.1.0 | 2023年5月18日 |
#34 在 #元编程
47KB
894 行
Rust 中的 Monads、Functors 及更多功能
Rust 语法风格的 Haskell 风格 monads。
语法
Rust 要求 >>=
具有自我修改能力,因此我们使用 >>
而不是 >>=
,并使用 consume
而不是 return
(关键字)。对于函子,您可以使用 fmap(f, x)
或 x.fmap(f)
,或者您可以将它 管道化:x | f | g | ...
。在当前情况下,像 Rust 这样的急切语言中,Haskell 的 monadic >>
似乎是不必要的,但我可能忽略了某些东西!
使用
只需编写一个 monad! { ...
并免费获得其所有超类,例如 Functor
,以及常见的派生,如 Debug
,Clone
,Eq
,Ord
,Hash
等,以及 enum
的所有成员都 pub use
了
use rsmonad::prelude::*;
monad! {
enum Maybe<A> {
Just(A),
Nothing,
}
fn bind(self, f) {
match self {
Just(a) => f(a),
Nothing => Nothing,
}
}
fn consume(a) {
Just(a)
}
}
// And these just work:
// Monad
assert_eq(Just(4) >> |x| u8::checked_add(x, 1).into(), Just(5));
assert_eq(Nothing >> |x| u8::checked_add(x, 1).into(), Nothing);
assert_eq(Just(255) >> |x| u8::checked_add(x, 1).into(), Nothing);
// Functor
assert_eq!(Just(4) | u8::is_power_of_two, Just(true));
assert_eq!(Nothing | u8::is_power_of_two, Nothing);
示例
无需担心细节即可捕获 panic
fn afraid_of_circles(x: u8) -> BlastDoor<()> {
if x == 0 { panic!("aaaaaa!"); }
Phew(())
}
assert_eq!(
Phew(42) >> afraid_of_circles,
Phew(())
);
assert_eq!(
Phew(0) >> afraid_of_circles,
Kaboom,
);
具有 Rust 向量速度的 Haskell 列表逻辑
// from the wonderful Haskell docs: https://wikibooks.cn/wiki/Haskell/Understanding_monads/List
fn bunny(s: &str) -> List<&str> {
List(vec![s, s, s])
}
assert_eq!(
List::consume("bunny") >> bunny,
List(vec!["bunny", "bunny", "bunny"]),
);
assert_eq!(
List::consume("bunny") >> bunny >> bunny,
List(vec!["bunny", "bunny", "bunny", "bunny", "bunny", "bunny", "bunny", "bunny", "bunny"]),
);
即使是最棘手的 join
在 bind
中的使用,也不需要任何类型注解。
let li = List::consume(List::consume(0_u8)); // List<List<u8>>
let joined = li.join(); // --> List<u8>!
assert_eq!(joined, List::consume(0_u8));
此外,我们自动推导出 QuickCheck::Arbitrary
并测试 monad 和函子定律。只需运行 cargo test
,它们就会与您所有的其他测试一起运行。
锋利边缘
目前,您只能在具有 Maybe
等类似 Monad
的具体实例时,将 >>
用作 bind
的糖语法,而不是一个通用的 <M: Monad<A>>
。后者仍然有效,但需要显式调用 m.bind(f)
(或者如果您没有使用 trait,Monad::<A>::bind(m, f)
)。这应该在 Rust 的非生命周期绑定功能推出时得到解决。
#![no_std]
禁用默认功能
# Cargo.toml
[dependencies]
rsmonad = { version = "*", default-features = false }
依赖关系
~275–720KB
~17K SLoC