【问题标题】:Exception handling in streams with Either使用 Either 的流中的异常处理
【发布时间】:2021-08-05 21:39:24
【问题描述】:

背景

我一直对Scott WLaschin's Railway Oriented Programming 处理异常的模型着迷:有一个侧通道,所有坏的东西都会被处理,而好的东西则保持在主轨道上。下图:

问题

日常代码中出现的常见模式如下:

  • 有数据列表
  • 验证每一个
  • 验证可能引发异常

问题

如何以类似于上面讨论的面向铁路的模型的方式做到这一点。

【问题讨论】:

    标签: java functional-programming vavr


    【解决方案1】:

    [Somjit's][1] 答案显示了正确的方法,但是它使用外部变量 (errors) 来累积错误。一般来说,这是不鼓励的,因为我们应该避免全局/外部状态。

    您可以使用 vavr 的 [partition][2] 方法将流分成两部分:一是有错误的,一是经过验证的整数。它们都被放入一个元组中:

    public void composeExceptions() {
       final Tuple2<Stream<Either<IllegalArgumentException, Integer>>, Stream<Either<IllegalArgumentException, Integer>>> both = Stream.range(1, 11)
                .map(this::validate)
                .partition(Either::isLeft);
    
       both._1.map(Either::getLeft).forEach(e -> System.out.println("Got error: " + e.getMessage()));
       both._2.map(Either::get).forEach(i -> System.out.println("Validated correctly: " + i));
    }
    

    编辑 实际上还有其他选择,例如:

    Stream
       .range(1, 11)
       .map(this::validate)
       .toJavaStream()
       .collect(Collectors.teeing(
          Collectors.filtering(Either::isLeft, toList()),
          Collectors.filtering(Either::isRight, toList()),
          (errors, ints) -> new Tuple2<>(errors.stream().map(Either::getLeft), ints.stream().map(Either::get))));
    

    它使用teeing,这是一个来自java API 的非常有趣的收集器。不幸的是,它混合了 vavr 和 java API,这不是很好,也不是很糟糕。

    还有:

    Stream
       .range(1, 11)
       .map(this::validate)
       .collect(
          () -> new Tuple2<>(List.<RuntimeException>empty().asJavaMutable(), List.<Integer>empty().asJavaMutable()),
          (tuple, either) -> {
             either.peekLeft(tuple._1::add);
             either.peek(tuple._2::add);
          },
          (t1, t2) -> {
             t1._1.addAll(t2._1);
             t1._2.addAll(t2._2);
          }
        )
           .map((exceptions, integers) -> new Tuple2<>(List.ofAll(exceptions), List.ofAll(integers)));```
    
    which uses vavr API only but underneath uses java `List` since a mutable structure is required here.
    
      [1]: https://stackoverflow.com/a/67556075/542270
      [2]: https://www.javadoc.io/doc/io.vavr/vavr/latest/io/vavr/collection/Traversable.html#partition(java.util.function.Predicate)
    

    【讨论】:

    • 不错!似乎目前仅在 alpha 版本中。希望我们很快就能看到它的通用版本
    • @Somjit 链接指向 alpha 版本,但它也包含在基本分支中。这是基本功能之一。
    【解决方案2】:

    处理“side track”异常的一种简单方法是使用 Vavr 提供的 peekleft 方法,该方法使用 Either.Left() 端。我们可以在那里插入我们的异常处理逻辑,并将我们的 Either.right() 东西很好地留在主轨道上,没有任何丑陋。

    我很确定这可以改进,并且会喜欢关于改进的想法。

    List<String> errors = new ArrayList<>();
    
    @Test
    public void composeExceptions() {
    
        List<Integer> valids = IntStream.range(0, 11).boxed()
                .map(this::validate)             // throws exceptions
                .peek(this::handleExceptions)    // process left/exceptions on the side
                .flatMap(Value::toJavaStream)    // flatmap is right based
                .collect(Collectors.toList());
    
        System.out.println("========= Good ones =========");
        System.out.println(valids);
        System.out.println("========= Bad Ones =========");
        errors.forEach(System.out::println);
    
    }
    
    public void handleExceptions(Either<IllegalArgumentException, Integer> either) {
        either.peekLeft(e -> errors.add(e.getMessage())); // is this a monadic bind ???
    }
    
    public Either<IllegalArgumentException, Integer> validate(Integer i) {
        if (i % 2 == 0) return Either.right(i);
        return Either.left(new IllegalArgumentException("odd one's out : " + i));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-16
      相关资源
      最近更新 更多