#逻辑 #元编程 #类型 #证明

无 std rsmonad-macros

Rust 风格的 monads

3 个版本

0.1.2 2023年5月19日
0.1.1 2023年5月19日
0.1.0 2023年5月18日

#34#元编程

LGPL-3.0-or-later

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,以及常见的派生,如 DebugCloneEqOrdHash 等,以及 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"]),
);

即使是最棘手的 joinbind 中的使用,也不需要任何类型注解。

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