JavaでList<Optional<T>>をOptional<List<T>>にする

#TL;DR
『Scala関数型デザイン&プログラミング』に出てくるsequence関数とtraverse関数のJava版
[Scala関数型デザイン&プログラミング―Scalazコントリビューターによる関数型徹底ガイド](https://www.amazon.co.jp/dp/B00WM54V5Q/ref=cm_sw_r_tw_dp_U_x_nZ3EDbNQJXBZE)


#やりたいこと(の内の1つ)
`Optional`のリストを受け取って、すべて値がある(= isPresentがtrue)なら`Optional`を外した値のリストにしたい。
もし1つでも値が`null`(= isPresentがfalse)なら、`Optional.empty`を返したい。

#実装
ざっくりと、こんな感じに書けると思います。
引数またはリストの要素にnullが入っていた場合はエラーになりますが、ここでは考慮しません。(~~nullを使うほうが悪い~~)

```java
public static <T> Optional<List<T>> sequence(List<Optional<T>> xs) {
    List<T> ys = new ArrayList<>();
    for (Optional<T> x : xs) {
        // 値が存在しない要素があったらその時点で処理を打ち切る
        if(x.isEmpty()) {
            return Optional.empty();
        }

        // 値が存在すればOptionalのラップを外してリストに追加
        ys.add(x.get());
    }

    // 値だけになったリストをOptionalでラップして返す
    return Optional.of(ys);
}
```

#使ってみる
```java
List<Optional<String>>  xs = List.of(Optional.of("foo"), Optional.of("bar"));
List<Optional<Integer>> ys = List.of(Optional.of(1), Optional.empty(), Optional.of(100));
List<Optional<Boolean>> zs = List.of(); // 空のリスト

System.out.println(sequence(xs)); // Optional[[foo, bar]]
System.out.println(sequence(ys)); // Optional.empty
System.out.println(sequence(zs)); // Optional[[]]
```

ちゃんと動いてるようです。


#応用
文字列を数値に変換する、以下の関数があるとします。
この関数は対象文字列が数値に変換できれば、その数値を`Optional`に包んで返します。
もし変換に失敗した場合は`Optional.empty`を返します。[^1]

```java
public static Optional<Integer> safeParseInt(String s) {
    try {
        Integer n = Integer.valueOf(s);
        return Optional.of(n);
    } catch (NumberFormatException e) {
        return Optional.empty();
    }
}
```

この関数を使用し、`String`のリストを`Optional<Integer>`のリストに変換します。
さらにsequence関数を適用したときのように、要素に`Optional.empty`が含まれる場合は全体の結果も`Optional.empty`にする関数を考えます。
名前は`traverse`とします。

```java
// これはすべて数値に変換可能なのでOptional[[1, 100, 30]]にしたい
Optional<List<Integer>> xs = traverse(List.of("1", "100", "30"));

// これは数値に変換できない値が混じっているのでOptional.emptyにしたい
Optional<List<Integer>> ys = traverse(List.of("1", "3", "hoge", "0", ""));
```

これは、先のsequence関数を使って、次のように実装できそうです。

```java
public static Optional<List<Integer>> traverse(List<String> xs) {
    // 一旦Optional<Integer>のリストに変換
    List<Optional<Integer>> ys = new ArrayList<>();
    for (String x : xs) {
        Optional<Integer> o = safeParseInt(x);
        ys.add(o);
    }

    // sequence関数でList<Optional<Integer>>をOptional<List<Integer>>に変換
    return sequence(ys);
}
```

動かしてみると、望んだ結果が得られていることがわかります。

```java
System.out.println(traverse(List.of("1", "100", "30")));          // Optional[[1, 100, 30]]
System.out.println(traverse(List.of("1", "3", "hoge", "0", ""))); // Optional.empty
```

しかし、上記は少しもったいない書き方をしています。
次の例を見てください。travase関数をインライン化し、途中結果も踏まえて出力しています。

```java
List<String> xs = List.of("1", "hoge", "2", "3", "4", "5", "6", "7", "8", "9");

// ここはtravarse関数と全く同じ処理
List<Optional<Integer>> ys = new ArrayList<>();
for (String x : xs) {
    Optional<Integer> o = safeParseInt(x);
    ys.add(o);
}
Optional<List<Integer>> zs = sequence(ys);

// 中間リストと結果を出力
System.out.println(ys); // [Optional[1], Optional.empty, Optional[2], Optional[3], Optional[4], Optional[5], Optional[6], Optional[7], Optional[8], Optional[9]]
System.out.println(zs); // Optional.empty
```

`zs`が`Optional.empty`になること自体は想定どおりです。
しかし、そのために`xs`と同じ長さの中間リスト`ys`が生成されてしまっています。
`xs`の2番目の要素`"hoge"`にsafeParseInt関数を適用し、`Optional.empty`が出来上がった時点で、全体の結果も`Optional.empty`となることは決定事項です。
結果がわかっているのにその後も変換処理を続けるのは、少し効率が悪いように感じます。
要素数が少なければ問題になることはあまりないと思いますが、要素数が多い場合はパフォーマンスに影響が出る可能性も考えられます。
`Optional.empty`になった時点でそれ以降の要素にはsafeParseInt関数を適用せず、ただちに`Optional.empty`が返ってくるようにしたいところです。

そうなるように、travarse関数を書き換えます。

```java
public static Optional<List<Integer>> traverse(List<String> xs) {
    List<Integer> ys = new ArrayList<>();
    for (String x : xs) {
        // 文字列を数値に変換
        Optional<Integer> o = safeParseInt(x);
    
        // 変換に失敗したらその時点で処理を打ち切る
        if(o.isEmpty()) {
            return Optional.empty();
        }

        // 変換に成功すればOptionalのラップを外してリストに追加
        ys.add(o.get());           
    }

    // 数値だけになったリストをOptionalでラップして返す
    return Optional.of(ys);
}
```

sequence関数を使うのをやめ、一回のループ内で変換処理と分岐の両方を行っています。
これで、途中で変換に失敗したらその時点でただちに`Optional.empty`が返ってくるようになりました。
safeParseIntをパラメータにして、`Optional`に変換できる関数なら何でも使えるようにします。

```java
public static <T, R> Optional<List<R>> traverse(List<T> xs, Function<T, Optional<R>> f) {
    List<R> ys = new ArrayList<>();
    for (T x : xs) {
        Optional<R> o = f.apply(x);
        if(o.isEmpty()) {
            return Optional.empty();
        }
        ys.add(o.get());
    }

    return Optional.of(ys);
}
```

使うときは、ラムダ式またはメソッド参照で渡します。
まとめると、次のようになります。

```Main.java
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        // ラムダ式
        Optional<List<Integer>> xs = traverse(List.of("1", "100", "30"), x -> safeParseInt(x));
        // メソッド参照
        Optional<List<Integer>> ys = traverse(List.of("1", "3", "hoge", "0"), Main::safeParseInt);

        System.out.println(xs); // Optional[[1, 100, 30]]
        System.out.println(ys); // Optional.empty
    }

    public static Optional<Integer> safeParseInt(String s) {
        try {
            Integer n = Integer.valueOf(s);
            return Optional.of(n);
        } catch (NumberFormatException e) {
            return Optional.empty();
        }
    }

    public static <T, R> Optional<List<R>> traverse(List<T> xs, Function<T, Optional<R>> f) {
        List<R> ys = new ArrayList<>();
        for (T x : xs) {
            Optional<R> o = f.apply(x);
            if(o.isEmpty()) {
                return Optional.empty();
            }
            ys.add(o.get());
        }
        return Optional.of(ys);
    }

// 使わなくなった
//    public static <T> Optional<List<T>> sequence(List<Optional<T>> xs) {
//        List<T> ys = new ArrayList<>();
//        for (Optional<T> x : xs) {
//            if(x.isEmpty()) {
//                return Optional.empty();
//            }
//            ys.add(x.get());
//        }
//        return Optional.of(ys);
//    }
}

```

#おまけ -- sequence関数の再実装
travarse関数からsequence関数を呼び出すのをやめましたが、実はこの新しいtravarse関数を使い、sequence関数を以下のように再実装できます。

```java
public static <T> Optional<List<T>> sequence(List<Optional<T>> xs) {
    return traverse(xs, Function.identity()); // traverse(xs, x -> x)でも可
}
```

少し不思議かもしれませんが、ここで`Function.identity()` (または`x -> x`[^2]) は`Optional<T>`を受け取り`Optional<T>`を返す関数であり、travarseの第二引数`Function<T, Optional<R>> f`は戻り値の型が`Optional`型であればいいので、上記のように書けます。[^3]

説明用に、travarse関数の型パラメータをこの新sequence関数に合わせてみます。

```java
public static <T> Optional<List<T>> traverse(List<Optional<T>> xs, Function<Optional<T>, Optional<T>> f) {
    List<T> ys = new ArrayList<>();
    for (Optional<T> x : xs) {
        Optional<T> o = f.apply(x); // fはFunction#identityなので、xとoは同一インスタンス
        if(o.isEmpty()) {
            return Optional.empty();
        }
        ys.add(o.get()); // 結局xの中身を詰めてるのと一緒
    }
    return Optional.of(ys);
}
```
最初に定義したsequence関数と同様、ただ`List<Optional<T>>`の各要素のOptionalを外しているだけとわかります。

#最後に
Javaではこういった関数を引数にするような汎用関数はあまり定義しないと思いますが(関数型インターフェースがたくさんあるので共通化しづらい)、何となく思い立ったので書いてみました。
実用性はともかく、頭の体操になって楽しいのでオススメです。

最後までお読み頂きありがとうございました｡
質問や不備についてはコメント欄か[Twitter](https://twitter.com/ka2_kamaboko)までお願いします。

[^1]: Optionalの中身の型に左右されない、汎用的な説明に繋げるため、OptionalIntではなOptional<Integer>を採用しています。
[^2]: `x -> x`のように書いてもいいのですが、この書き方だと毎回`Function<T, T>`型のオブジェクトが生成されてしまうため、特に理由がなければ`Function.identity()`を使っておいた方が良いと思います。
[^3]: 型パラメータの`T`が被っているので混乱するかもしれませんが、ここのtravarseの`T`とsequenceの`T`が指しているものは別の型です。travarseの`T`が、sequenceでは`Optional<T>`にあたります。travarseの型パラメータ`T,R`は同じ型でもいいので、`Function<Optional<T>, Optional<T>>`型はtravarseの第二引数の要件を満たしています。