深入解析 Monad(5)Writer Monad
- UID
- 1066743
|
深入解析 Monad(5)Writer Monad
Writer Monad 展示了 Writer Monad 的一种用法,也就是累积 count 的值。实际上,Writer Monad 的主要作用是在函数调用过程中收集辅助信息,比如日志信息或是性能计数器等。其基本的思想是把副作用中对外部环境的修改聚合起来,从而把副作用从函数中分离出来。聚合的方式取决于所产生的副作用。 中的副作用是修改计算器 count,相应的聚合方式是累加计数值。如果副作用是产生日志,相应的聚合方式是连接日志记录的字符串。聚合方式是每个 Writer Monad 的核心。对于聚合方式的要求和范畴中对于态射的要求是一样,也就是必须是传递的,而且有组合的基本单元。在 中,聚合方式是 Integer 类型的相加操作,是传递的;同时也有基本单元,也就是加零。
下面对 Writer Monad 进行更加形式化的说明。Writer Monad 除了其本身的类型 T 之外,还有另外一个辅助类型 W,用来表示聚合值。对类型 W 的要求是前面提到的两点,也就是存在传递的组合操作和基本单元。Writer Monad 的 unit 操作比较简单,返回的是类型 T 的值 t 和类型 W 的基本单元。而 bind 操作则需要分别转换类型 T 和 W 的值。对于 T 的值,按照 Monad 自身的定义来转换;而对于 W 的值,则使用该类型的传递操作来聚合值。聚合的结果作为转换之后的新的 W 的值。
清单 7 中是记录日志的 Writer Monad 的实例。该 Monad 自身的类型使用 Java 泛型类型 T 来表示,而辅助类型是 List<String>,用来保存记录的日志。List<String> 满足作为辅助类型的要求。List<String> 上的相加操作是传递的,也存在作为基本单元的空列表。LoggingMonad 中的 unit 方法返回传入的值 value 和空列表。bind 方法的第一个参数是 LoggingMonad<T1> 类型,作为变换的输入;第二个参数是 Function<T1, LoggingMonad<T2>> 类型,用来把类型 T1 转换成新的 LoggingMonad<T2> 类型。辅助类型 List<String> 中的值通过列表相加的方式进行组合。方法 pipeline 表示一个处理流水线,对于一个输入 Monad,依次应用指定的变换,得到最终的结果。在使用示例中,LoggingMonad 中封装的是 Integer 类型,第一个转换把值乘以 4,第二个变换把值除以 2。每个变换都记录自己的日志。在运行流水线之后,得到的结果包含了转换之后的值和聚合的日志。
清单 7. 记录日志的 Monad1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| public class LoggingMonad<T> {
private final T value;
private final List<String> logs;
public LoggingMonad(T value, List<String> logs) {
this.value = value;
this.logs = logs;
}
@Override
public String toString() {
return "LoggingMonad{" +
"value=" + value +
", logs=" + logs +
'}';
}
public static <T> LoggingMonad<T> unit(T value) {
return new LoggingMonad<>(value, List.of());
}
public static <T1, T2> LoggingMonad<T2> bind(LoggingMonad<T1> input,
Function<T1, LoggingMonad<T2>> transform) {
final LoggingMonad<T2> result = transform.apply(input.value);
List<String> logs = new ArrayList<>(input.logs);
logs.addAll(result.logs);
return new LoggingMonad<>(result.value, logs);
}
public static <T> LoggingMonad<T> pipeline(LoggingMonad<T> monad,
List<Function<T, LoggingMonad<T>>> transforms) {
LoggingMonad<T> result = monad;
for (Function<T, LoggingMonad<T>> transform : transforms) {
result = bind(result, transform);
}
return result;
}
public static void main(String[] args) {
Function<Integer, LoggingMonad<Integer>> transform1 =
v -> new LoggingMonad<>(v * 4, List.of(v + " * 4"));
Function<Integer, LoggingMonad<Integer>> transform2 =
v -> new LoggingMonad<>(v / 2, List.of(v + " / 2"));
final LoggingMonad<Integer> result =
pipeline(LoggingMonad.unit(8),
List.of(transform1, transform2));
System.out.println(result); // 输出为 LoggingMonad{value=16,
logs=[8 * 4, 32 / 2]}
}
}
|
|
|
|
|
|
|