Javaの関数型プログラミングライブラリVavrについて

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

今取り組んでいるプロジェクトで久々にJavaのコードを書いていたらVavrというライブラリを使っているのを見つけました。 使ったことがないライブラリだったのですが、面白かったので少し調べてみました。 興味が続く限りネタにしてみたいと思います。

Vavrとは

ドキュメントによると次のデータ型や制御機能を提供することでJavaでの関数型プログラミングを実現します。

  • データ型
    • ScalaのIterable相当のコレクション(イミュータブルで永続的なデータ構造)
    • Stream
    • Tuple
  • Function(arity 0-8の関数オブジェクト。合成したり、リフティングやカリー化もサポートしている)
  • モナド
    • Option
    • Try
    • Lazy
    • Either
    • Future
    • Validation
  • パターンマッチ

ちなみに公式ページには以下のように説明されています。

Vavr core is a functional library for Java. It helps to reduce the amount of code and to increase the robustness. A first step towards functional programming is to start thinking in immutable values. Vavr provides immutable collections and the necessary functions and control structures to operate on these values. The results are beautiful and just work.

すごい!便利そう!!

どうやって使ったらいいのか?

とても便利そうですが、コレクション、ストリーム、Option(Optional)、FutureについてはJavaの標準ライブラリに類似したAPIがあるし、互換性や、将来の変更を考えるとVavrをコンポーネントの公開APIのシグネチャに使うには不安があります。

どのように使うのか検討するために標準ライブラリや他のライブラリでの類似の実装を比較してみようと思います。

各ライブラリでのOptionの比較

手始めによく使うOptionの比較をしてみます。

プロジェクトで使っているライブラリから3つの実装をピックアップしました。

  • guava (com.google.common.base.Optional<T> )
  • vavr (io.vavr.control.Option<T>)
  • java標準ライブラリ(java.util.Optional<T>)

以下ではguavaとvavrの特徴を整理しています。

com.google.common.base.Optional

guavaのOptionalはJDK8でOptionalが導入される以前から存在していました。 その経緯からか、ドキュメントには下記のように標準ライブラリとの比較が書かれています。

  • This class is serializable; java.util.Optional is not.
  • java.util.Optional has the additional methods ifPresent, filter, flatMap, and orElseThrow.
  • java.util offers the primitive-specialized versions OptionalInt, OptionalLong and OptionalDouble, the use of which is recommended; Guava does not have these.

個人的には次の差異が気になりました。

  • transform(map相当)のnullの扱い
    • 関数がnullを返すと例外をスローする、標準ライブラリはemptyを返す
  • filter, flatMapがない
    • これはどう考えても欲しい
  • toSetが便利かもしれない
    • 値があるときはその値を含むセット、ないときは空のセットを返す
    • 有効な値がないときに空のコレクションを使うパターンで使えるがOptionalをそのまま使うから要らない気もする・・・
  • presentInstances(Iterable<Optional>)
    • 引数のIterable<Optional>のうち値が存在するものだけを返す
    • 便利そうだけど(ドキュメントにも書かれてい通り)コレクションにfilterがあれば要らない

ドキュメントには次のように標準ライブラリ使ってね、という記載があるので、新たに書き始めるコードでは使わないほうが良さそうです。

There are no plans to deprecate this class in the foreseeable future. However, we do gently recommend that you prefer the new, standard Java class whenever possible.

io.vavr.control.Option

ドキュメントでは下記のように紹介されています

  • 具象型としてSome, None が定義されている(他の実装では公開されている型はOptionalだけで、具象クラスはパッケージプライベートか、そもそもOptional自体が具象クラスになっている)
  • mapの引数の関数がnullを返すときemptyに変換されずnullを保持する

特に後者について、設計の背景がVarvrブログ「The agonizing death of an astronaut」こちらの記事「Why java.util.Optional is broken」で説明されています。

一旦まとめ

さて、ここまでで次のことがわかりました。

  • 値がない場合のmapの振る舞いが3つのライブラリで異なる
  • 標準ライブラリのOptional#mapの「nullをemptyに置き換えてしまう挙動」を問題視する意見がある

このmapの問題について検討しないと、どのような場合に使える/問題があるのかはっきりしません。 今回は長くなってきたのでここまでにして、改めてこの問題についてまとめたいと思います。