Monad in Rust

Monad in Rust 不是好文明

声明:本文全属口嗨。

群友又在讨论在Rust中搞Monad了。于是写下了自己一些想法。

 

洋葱结构是Rust中十分常见的类型嵌套结构。洋葱结构即effect序列:类型T1<T2<T3<...Tn<A>...>>表示带有effect序列T1, T2, T3, ...,TnA类型的计算

如果有Tn: Foo满足结合律[1]且有单位effectimpl<A> Once<A> for Foo { type Output = A; ... },则Foo是个Monad。

但由于lifetime只能由“洋葱”的外侧传到内侧,外层的(左边的)的effect依赖内层的(右边的)effect。

所以在T1<T2<T3<...Tn<A>...>>'static的情况下,只有右结合而没有左结合(外层的effect不能独立存在)。没有结合律就谈不上Monad了。

 

左结合出问题的情况,最常见的情况是在使用组合子式API时出现的lifetime问题。

其本质就是左结合导致的自引用的问题(破坏了lifetime的传递规则,使内层依赖外层),这时无法通过组合子式的API来解决。这时候就需要手动将两个effect合并到一个结构里,并Pin起来。async-await便是编译器帮你自动完成该工作的一个语法糖。

 

其实不应该说“不满足结合律”,而是有些需要左结合的 类型 其 项 根本不合法。这就大大限制了Monad在Rust的应用面(又没有通用的do,自动自引用)。不过Rust自带控制流和side-effect不香吗,future还有async-await的语法糖,干嘛非得搞组合子呢。噢对了,还有一个解决方向,就是提倡无lifetime编程(这是可以做到的)。

 

于是,由于lifetime的存在,Monad in Rust确实不是好文明。(但Functor是)

 

[1]: 结合律不知道在洋葱层面如何表达。但impl<In: Foo> Tn<In> for Foo { type Output = In::Output; ... }应该天然表示右结合。或许trait FooFamily { type Foos<A>: Foo<Output = A> ...}可以表达左结合?

不过用flat_map之类的可以表示结合律: