Vavr:如何将flatmap集合放在可选对象中

Mar*_*szS 2 java java-stream vavr

是否有最简单的方法可以在下面编写此代码而不使用toStream()

import io.vavr.collection.List;
import io.vavr.control.Option;
import lombok.Value;

public class VavrDemo {

    public static void main(String[] args) {
        Foo bar = new Foo(List.of(new Bar(1), new Bar(2)));
        Number value = Option.some(bar)
                .toStream()              // <- WTF?!?
                .flatMap(Foo::getBars)
                .map(Bar::getValue)
                .sum();
        System.out.println(value);
    }

    @Value
    static class Foo {
        private List<Bar> bars;
    }

    @Value
    static class Bar {
        private int value;
    }
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*ich 12

选项是所谓的Monad.这只是告诉我们flatMap函数遵循特定的规律,即

  • A,B,C是类型
  • unit:A - > Monad <A>一个构造函数
  • f:A - > Monad <B>,g:B - > Monad <C>功能
  • a是A类型的对象
  • m是Monad <A>类型的对象

然后,Monad接口的所有实例都应遵守Functor定律(这里省略)和三个控制定律:

  • 左侧身份: unit(a).flatMap(f) ? f a
  • 正确的身份: m.flatMap(unit) ? m
  • 关联性: m.flatMap(f).flatMap(g) ? m.flatMap(x -> f.apply(x).flatMap(g))

目前Vavr已经(简化):

interface Option<T> {
    <U> Option<U> flatMap(Function<T, Option<U>> mapper) {
        return isEmpty() ? none() : mapper.apply(get());
    }
}
Run Code Online (Sandbox Code Playgroud)

此版本遵守Monad法律.

无法按照您希望的方式定义Option.flatMap仍然遵守Monad法则.例如,想象一个flatMap版本,它接受一个带有Iterable的函数作为结果.所有Vavr集合都有这样的flatMap方法,但对于Option,它没有意义:

interface Option<T> {
    <U> Option<U> flatMap(Function<T, Iterable<U>> mapper) {
        if (isEmpty()) {
            return none();
        } else {
            Iterable<U> iterable = mapper.apply(get());
            if (isEmpty(iterable)) {
                return none();
            } else {
                U resultValue = whatToDoWith(iterable); // ???
                return some(resultValue);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你看?我们可以做的最好的事情是只获取iterable中的一个元素,以防它不为空.除了它没有使用你可能期望的结果(在上面的VavrTest中),我们可以证明这个'幻想'版本的flatMap确实打破了Monad法则.

如果你遇到这种情况,请考虑稍微改变你的电话.例如,VavrTest可以表示如下:

Number value = Option.some(bar)
    .map(b -> b.getBars().map(Bar::getValue).sum())
    .getOrElse(0);
Run Code Online (Sandbox Code Playgroud)

我希望这有帮助,上面的Monad部分并没有完全吓跑你.实际上,开发人员不需要了解Monads的任何信息就可以利用Vavr.

免责声明:我是Vavr的创始人(原名:Javaslang)