51 个版本 (4 个破坏性更新)
0.5.5 | 2019 年 12 月 29 日 |
---|---|
0.5.4 | 2019 年 12 月 20 日 |
0.4.10 | 2019 年 12 月 11 日 |
0.3.14 | 2019 年 12 月 3 日 |
0.1.16 | 2019 年 11 月 22 日 |
#594 在 Rust 模式
每月 98 次下载
53KB
623 行
rust-monadic
- 基于 Bind 和 Monad 作为 IntoIterator (可迭代) 的超特质的单子块宏
- Reader 单子块宏
- Writer 单子块宏
- State 单子块宏
- ReaderT 单子变换块宏
- WriterT 单子变换块宏
- StateT 单子变换块宏
mdo! 宏
用于编写 Haskell 风格单子代码的宏
适用于 IntoIterator (可迭代) 作为单子
每个单子表达式步骤都是一个 flat_mapped 表达式,与剩余部分合并成一个惰性 FlatMap 表达式,该表达式实现 IntoIterator,使用捕获环境和参数的 move 闭包 作为 Fn(A) -> U: IntoIterator
。lambda 体将递归解析为单子,其类型也应该是 IntoIterator 的实例。
除了实现 IntoIterator 的类型外,所有迭代器都按照 文档 中所述进行。
在 monad 模块中定义了 Bind 和 Monad 特性,作为 IntoIterator 的超特性。
以下是宏 mdo
的模式表,其中 monadic_expression 是类型之一,必须是 IntoIterator 的实例
* 绑定单子结果 | `identifier "<-" monadic_expression ";"` |
---|---|
* 提升值并绑定 | `identifier "<-" "pure" expression ";"` |
* 解引用共享可迭代项的项引用 | `"&" identifier "<-" &iterable ";"` |
* 组合单子结果 | `"let" identifier "=" expression ";"` |
* 过滤结果 | `"guard" boolean_expression ";"` |
* 忽略单子结果 | “_” <-- monadic_expression ";" |
返回表达式值 | “pure” return_expression |
以单子表达式结束 | monadic_expression |
注意:在宏内部,let 只引入一个绑定。
示例1:类似于 Haskell 的单子推导(文件:examples/comprehension.rs)
use monadic::{mdo, monad::{Bind, Monad}};
use num::Integer;
fn main() {
let xs = mdo!{
x <- 1..7;
y <- 1..x;
guard (&y).is_odd() ;
let z = match x.is_even() {
true => &y + 1,
_ => &y - 1,
};
pure (x, z)
}.collect::<Vec<_>>();
println!("result: {:?}", xs);
}
执行
$ cargo run --example comprehension
result: [(2, 2), (3, 0), (4, 2), (4, 4), (5, 0), (5, 2), (6, 2), (6, 4), (6, 6)]
示例2:引用容器和 lambda 参数位置的变体(文件:examples/comprehension2.rs)
use monadic::{mdo, monad::{Bind, Monad}};
use num::Integer;
fn main() {
let xs = mdo!{
&x <- &vec![1,2,3,4]; // with item ref pattern (&x) in the lambda argument position
guard x.is_odd() ;
let z = x + 1 ;
pure (x, z)
}.collect::<Vec<_>>();
println!("result: {:?}", xs);
}
执行
$ cargo run --example comprehension2
result: [(1, 2), (3, 4)]
示例:控制台 I/O。如果您想返回 String 变量,可以通过克隆来实现
// example console io
use monadic::{mdo, monad::{Bind, Monad},
mio::{read_line, print_str, stdout_flush}};
fn main() {
let res =mdo!{
x <- pure 1;
let y = x + 1;
_ <- print_str("enter integer i32>");
_ <- stdout_flush();
li1 <- read_line();
z <- li1.trim().parse::<i32>() ;
pure (y, z, li1.clone())
}.collect::<Vec<_>>();
println!("result: {:?}", res);
}
$ cargo run --example console_io
enter integer i32>10
result: [(2, 10, "10")]
Reader 单子宏 rdrdo!
一个 Reader 单子 适配宏示例
//! examples/reader1
//!
//! You must specify in a type restriction the type of the environment of the Reader bloc
//!
//! `local` can be used as a function or as a method
use monadic::{rdrdo, reader::{Reader, ask, local}};
use partial_application::partial;
use std::collections::HashMap;
type Env = HashMap<String, i32>;
fn immutable_insert( k_slice: &str, v: i32, dict: Env) -> Env {
let mut dict1 = dict.clone();
dict1.insert( String::from(k_slice), v);
dict1
}
fn my_initial_env() -> Env {
immutable_insert( "a", 1, HashMap::new())
}
fn main() {
let modify_env = partial!(immutable_insert => "b", 2, _);
let bloc1: Reader<'_, Env, _> = rdrdo!{
env1 <- ask();
// run a subbloc with a modified environment
pair <- local( modify_env, rdrdo!{
x <- pure 9;
y <- ask();
pure (x, y)
}) ;
pure (env1.clone(), pair.0, pair.1)
};
let res = bloc1.initial_env( my_initial_env() );
println!("result: {:?}", res);
}
执行
$ cargo run --example reader1
result: ({"a": 1}, 9, {"b": 2, "a": 1})
ReaderT 单子变换宏 rdrt_mdo!
这个单子变换器是严格的,并且只适用于实现了 Monad + FromIterator + Clone
的内部单子,仅限于 Vec、LinkedList 和 VecDeque。您可以使用 lift
与任意单子混合指令,因为绑定是通过迭代 IntoIterator 的 into_iter().flat_map().collect()
来发生的。
这个宏需要更多的类型注解,因为内部单子和 lambda 参数可能是不确定的。
为了减少类型注解,它们通过宏插入 ask()
,使用 Env
作为环境类型别名,它必须被定义。
pure return_expression
通过宏转换为 ReaderT::lift( Vec::pure( return_expression))
示例
// examples/reader_trans1
#[allow(unused_imports)]
use monadic::{rdrt_mdo, monad::{Monad},
reader_trans::{ReaderT, ask, local}};
use num::Integer;
use partial_application::partial;
use std::collections::HashMap;
/// mandatory type alias Env as it is used in the macro
/// to save you type annotations
type Env = HashMap<String, i32>;
fn immutable_insert( k_slice: &str, v: i32, dict: Env) -> Env {
let mut dict1 = dict.clone();
dict1.insert( String::from(k_slice), v);
dict1
}
fn my_initial_env() -> Env {
immutable_insert( "a", 1, HashMap::new())
}
fn main() {
let modify_env = partial!(immutable_insert => "b", 2, _);
// example with Vec as the nested monad
let bloc = rdrt_mdo!{ // possible type restriction as ReaderT<'_, Env, Vec<_>>
env1 <- ask(); // the macro adds the type annotation as ReaderT<'_, Env, Vec<Env>>
// run a subblock with a modified env.
pair <- local( modify_env, rdrt_mdo!{
// x <- lift (5..9).collect::<Vec<_>>();
x <- lift_iter 5..9;
guard x.is_odd();
let z = x + 1;
y <- ask();
pure (z, y) // equivalent to lift Vec::pure((z, y))
}) ;
pure (env1.clone(), pair.0, pair.1)
};
// applying the initial_env() to the transformer (env -> m a)
// returns the nested monad structure
let res = bloc.initial_env( my_initial_env() );
println!("result: {:?}", res);
}
执行
$ cargo run --example reader_trans1
result: [({"a": 1}, 6, {"a": 1, "b": 2}), ({"a": 1}, 8, {"a": 1, "b": 2})]
Writer 单子宏 wrdo!
一个 Writer 单子 适配宏示例,使用 String 作为日志记录器,来自 examples/writer1.rs
//! examples/writer1.rs
//!
//! you may set the logger type
//! by beginning with a `tell...` function within the macro `wrdo`
//! or by declaring it as the result type
//! where String is the default if omitted
//! as in `let res : Writer< _, String > = wrdo!{...}`
//!
//! `censor(), listen() and listens()` can be used as functions or as methods of a Writer bloc
#[allow(unused_imports)]
use monadic::{wrdo, writer::{Writer, tell, tell_str, censor, listen}};
use monadic::util::concat_string_str;
use partial_application::partial;
type Log = String;
fn main() {
let modify_log = partial!( concat_string_str => _, "log2");
let res : Writer< _, Log> = wrdo!{
_ <- tell_str( "log1") ;
// run a subbloc and modify the log afterwards
pair <- censor( modify_log,
wrdo!{
_ <- tell_str("sub");
pure 2
}.listen());
pure pair
}.listen() ;
println!("result: {:?}", res.unwrap());
}
执行
$ cargo run --example writer1
result: ((2, "sub"), "log1sublog2")
示例 2 使用 Vec 作为日志记录器,来自 examples/writer2.rs
//! examples/writer2.rs
//!
//! you may set the logger type
//! by beginning with a `tell...` function within the macro `wrdo`
//! or by declaring it as the result type
//! where String is the default if omitted
//! as in `let res : Writer< _, Vec<_> > = wrdo!{...}`
//!
//! `censor(), listen() and listens()` can be used as functions or as methods of a Writer bloc
#[allow(unused_imports)]
use monadic::{wrdo, writer::{Writer, tell, censor, listen}};
use monadic::util::concat_vec_array;
use partial_application::partial;
type Log = Vec<i32>;
fn main() {
let modify_log = partial!( concat_vec_array => _, &[4,5,6]);
let res : Writer< _, Log> = wrdo!{
_ <- tell( vec![1,2,3]) ;
// run a subbloc and modify the log afterwards
pair <- censor( modify_log,
wrdo!{
_ <- tell( vec![0]) ;
pure 2
}.listen());
pure pair
}.listen() ;
println!("result: {:?}", res.unwrap());
}
$ cargo run --example writer2
result: ((2, [0]), [1, 2, 3, 0, 4, 5, 6])
WriterT 单子变换宏 wrt_mdo!
仅适用于 Vec、LinkedList 或 VecDeque 作为内部单子。您可以提升任意单子的表达式,因为绑定是通过迭代和收集来完成的。
添加了宏关键字 tell_str、tell_array、tell_vec、tell_string,它们可以保存对单子的类型注解,因为宏输出会为您完成这项工作。它们使用宏输出类型注解中的 Log
类型别名。
现在,关键字 pure return_expresion
通过 Vec::pure(return_expression) 提升返回表达式
示例
//! examples/writer_trans1.rs
//!
//! you may set the logger type
//! by beginning with a `tell...` function within the macro `wrdo`
//! or by declaring it as the result type
//! where String is the default if omitted
//! as in `let res : WriterT< _, Log > = wrdo!{...}`
//!
//! `censor(), listen() and listens()` can be used as functions or as methods of a Writer bloc
#[allow(unused_imports)]
use monadic::{wrt_mdo, monad::Monad, writer_trans::{WriterT, tell, tell_str, tell_array, censor, listen}};
use monadic::util::concat_string_str;
use partial_application::partial;
use num::Integer;
#[allow(dead_code)]
type Log = String; // used in some macro constructs
fn main() {
let modify_log = partial!( concat_string_str => _, "log2");
let bloc = wrt_mdo!{ // : WriterT< Vec<_>> // type param. `log` defaults to String
_ <- tell_str "log1" ;
// x <- lift (5..9).collect::<Vec<_>>() ;
x <- lift_iter 5..9 ;
guard x.is_odd() ;
let z = x + 1;
// run a subbloc and modify its log afterwards
pair <- censor( modify_log,
wrt_mdo!{
_ <- tell_str "sub";
pure 2
}.listen()
);
pure (z, pair.0, pair.1)
}.listen() ;
// unwrap() returns the nested monad structure
let res = bloc.unwrap();
println!("result: {:?}", res);
}
执行
$ cargo run --example writer_trans1
result: [((6, 2, "sub"), "log1sublog2"), ((8, 2, "sub"), "log1sublog2")]
使用 Vec 作为日志记录器的示例
//! examples/writer_trans2.rs
//!
//! you may set the logger type
//! by beginning with a `tell...` function within the macro `wrdo`
//! or by declaring it as the result type
//! where String is the default if omitted
//! as in `let res : WriterT< _, Vec<_>> = wrdo!{...}`
//!
//! `censor(), listen() and listens()` can be used as functions or as methods of a Writer bloc
#[allow(unused_imports)]
use monadic::{wrt_mdo, monad::Monad, writer_trans::{WriterT, tell, tell_str, tell_array, censor, listen}};
use monadic::util::concat_vec_array;
use partial_application::partial;
use num::Integer;
/// mandatory type alias Log only if it is not the default
/// as it is used in the macro
/// to save you type annotations
type Log = Vec<i32>;
fn main() {
let modify_log = partial!( concat_vec_array => _, &[4,5,6]);
let bloc = wrt_mdo!{ // : WriterT< Vec<_>, Log>
_ <- tell_array &[1,2,3] ;
x <- lift (5..9).collect::<Vec<_>>() ;
guard x.is_odd() ;
let z = x + 1;
// run a subbloc and modify its log afterwards
pair <- censor( modify_log,
wrt_mdo!{
_ <- tell_array &[0];
pure 2
}.listen()
);
pure (z, pair.0, pair.1)
}.listen() ;
// unwrap() returns the nested monad structure
let res = bloc.unwrap();
println!("result: {:?}", res);
}
$ cargo run --example writer_trans2
result: [((6, 2, [0]), [1, 2, 3, 0, 4, 5, 6]), ((8, 2, [0]), [1, 2, 3, 0, 4, 5, 6])]
State 单子宏 stdo!
一个 State 单子 适配宏示例,来自 examples/state1.rs
//! examples/state1.rs
//!
//! You may specify in a type restriction the type of the State bloc
//! or apply it directly to an initial_state without the type restriction
use monadic::{stdo, state::{State, get, put}};
type St = i32;
fn main() {
let bloc: State<'_, St, _> = stdo!{
x <- pure 9;
y <- get();
_ <- put( 1);
z <- get();
pure (x, y, z)
};
let res = bloc.initial_state(0) ;
println!("result: {:?}", res);
}
执行。
$ cargo run --example state1
result: ((9, 0, 1), 1)
StateT 单子变换宏 stt_mdo!
use monadic::{stt_mdo, state_trans::{StateT, get, put}};
use num::Integer;
// mandatory type alias as it is used within the macro for type annotations
type St = i32;
fn main() {
let bloc = stt_mdo!{ // : StateT<'_, St, Vec<_>, _> // StateT<'a, St, Monad, A>
// x <- lift (5..9).collect::<Vec<_>>() ;
x <- lift_iter 5..9 ; // lift_iter iterator
guard x.is_odd();
y <- get() ;
_ <- put( 1) ;
z <- get() ;
let v = x +1 ;
pure (v, y, z)
};
// returns the monad within the transformer boxed function (s -> m (a,s))
let res = bloc.initial_state( 0);
println!("result: {:?}", res);
}
执行
$ cargo run --example state_trans1
result: [((6, 0, 1), 1), ((8, 0, 1), 1)]
一些测试
$ cargo test
running 1 test
test monad::tests::prop_monad_comprehension_vs_iteration ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
更改
v. 0.5.5: 清理说明
v. 0.5.4: 说明中存在错别字
v. 0.5.3: 基于功能的条件编译,功能 ["reader", "reader_trans", "writer", "writer_trans", "state", "state_trans"]。默认情况下,所有功能都会编译。模块 Monad 以及其宏 "mdo" 是无条件编译的。
[dependencies.monadic]
version = "~0.5"
default-features = false
features = ["reader_trans"] # pick the modules of your interest.
v. 0.5.2: 向 ReaderT 和 WriterT 添加了 lift_iter,以及在宏中的引用绑定模式 (&v)
v. 0.5.1: StateT 变换器宏
v. 0.5.0: 更新 ReaderT 和 WriterT 变换器宏以减少类型注解的数量
- 宏生产 "pure" $expr 转换为 lift(Vec::pure($exp))
- ReaderT 宏生成器 "$v <- ask()" 在输出中使用类型别名 Env 生成类型注解。
- WriterT 宏生成器 "_ <- tell_.. $expr" 在输出中使用类型别名 Log 生成类型注解。
v. 0.4.10: 向 ReaderT 和 WriterT 转换宏添加了 let 绑定。
v. 0.4.9: 修正了 README。
v. 0.4.8: 为 (Vec, LinkedList, VecDeque) 添加了 WriterT 转换器作为嵌套 monads。
v. 0.4.7: 为 (Vec, LinkedList, VecDeque) 添加了 ReaderT 转换器作为嵌套 monads。
v. 0.4.5 和 0.4.6: 清理了文档。
v. 0.4.4: 清理了旧的 intoiter 宏引用。抑制了实验性的 MonadPlus,它尚未准备好。
v. 0.4.3: 修正了 README 中的拼写错误。
v. 0.4.2: 添加了带有 quickcheck 测试的 MonadPlus。
v. 0.4.1: 使用克隆展示 String 返回的 console_io 示例。
v. 0.4.0
- 将 writer 函数
censor_do
重命名为 censor。 - 添加了 writer 函数 listen() 和 listens()。
- 将 local_do() 重命名为 local()。
- 移除了 intoiter 模块,因为它在功能上重复,且没有增加适用性,请使用模块 monad 的
mdo
宏代替。
v. 0.3.14: 添加了 writer 函数 censor_do
。
v. 0.3.13: 添加了 reader 函数 local_do
。
v. 0.3.12: 简化了 reader1 示例。
v. 0.3.11: 从 Writer 和 State monads 中抑制了形式 "&v <- ..."。
v. 0.3.10: 添加了 Reader 宏。它可以在可克隆环境中运行良好,例如 HashMap。State 宏已更新,使用非静态生命周期为boxed闭包。
v. 0.3.9: 添加了 (<-) 右侧的 pure
。