首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

深入解析 Monad(5)Writer Monad

深入解析 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. 记录日志的        Monad
1
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]}
  }
}

返回列表