1前書き


Fugue

はAtlassianによるJavaライブラリです。それは

関数型プログラミング

をサポートするユーティリティの集まりです。

今回の記事では、最も重要なFugueのAPIに焦点を合わせて探ります。

** 2 Fugueを使ってみる

**

私たちのプロジェクトでFugueを使い始めるには、次の依存関係を追加する必要があります。

<dependency>
    <groupId>io.atlassian.fugue</groupId>
    <artifactId>fugue</artifactId>
    <version>4.5.1</version>
</dependency>

** 3

オプション


**


java.util.Optional.

に対するFugueの答えである

Option

クラスを見て、旅を始めましょう。

名前から推測できるように、**

__

Optionは、潜在的に存在しない値を表すコンテナーです。

つまり、

Option

は、特定の型の

Some

値か

None

のいずれかです。

Option<Object> none = Option.none();
assertFalse(none.isDefined());

Option<String> some = Option.some("value");
assertTrue(some.isDefined());
assertEquals("value", some.get());

Option<Integer> maybe = Option.option(someInputValue);

** 3.1.

map

操作+

**

標準の関数型プログラミングAPIの1つは、提供された関数を基礎となる要素に適用できるようにする

map()

メソッドです。

このメソッドは、指定された関数が

Option

‘s値に存在する場合はそれに適用します。

Option<String> some = Option.some("value")
  .map(String::toUpperCase);
assertEquals("VALUE", some.get());


3.2.

Option



Null

Value

命名の違いに加えて、アトラシアンは

Option

に対して

Optional

とは異なるいくつかの設計上の選択を行いました。それでは、それらを見てみましょう。


  • null

    値を保持する空でない

    Option

    を直接作成することはできません** :

Option.some(null);

上記は例外を投げます。

  • しかし、

    map()

    操作を使用した結果として取得することができます。

Option<Object> some = Option.some("value")
  .map(x -> null);
assertNull(some.get());

単に

java.util.Optional.

を使用しただけでは、これは不可能です。

** 3.3.

Option

は__Iterable +です

__ **


Option

は、最大1つの要素を保持するコレクションとして扱うことができるので、

Iterable

インターフェースを実装することは意味があります。

これにより、コレクションやストリームを扱う際の相互運用性が大幅に向上します。

そして今、例えば、他のコレクションと連結することができます。

Option<String> some = Option.some("value");
Iterable<String> strings = Iterables
  .concat(some, Arrays.asList("a", "b", "c"));

** 3.4.

Option

から

Stream

への変換

**


Option



Iterableなので、

Stream__にも簡単に変換できます。

変換後、

Stream

インスタンスは、オプションが存在すれば正確に1つの要素を、そうでなければゼロを持ちます。

assertEquals(0, Option.none().toStream().count());
assertEquals(1, Option.some("value").toStream().count());

** 3.5.

java.util.Optional

相互運用性+

**

標準の

Optional

実装が必要な場合は、__toOptional()メソッドを使って簡単に取得できます。

Optional<Object> optional = Option.none()
  .toOptional();
assertTrue(Option.fromOptional(optional)
  .isEmpty());

** 3.6.

Options

ユーティリティクラス+

**

最後に、Fugueは適切な名前の

Options

クラスで

__Option

__sを扱うためのユーティリティメソッドをいくつか提供しています。

コレクションから空の

Options

を削除するための

filterNone

、および

Options

のコレクションを囲まれたオブジェクトのコレクションに変換するための

flatten

などのメソッドを備えています。

さらに、

Function <A、B>



Function <Option <A>、Option <B>>

に持ち上げる、

lift

メソッドのいくつかの変形があります。

Function<Integer, Integer> f = (Integer x) -> x > 0 ? x + 1 : null;
Function<Option<Integer>, Option<Integer>> lifted = Options.lift(f);

assertEquals(2, (long) lifted.apply(Option.some(1)).get());
assertTrue(lifted.apply(Option.none()).isEmpty());

これは、

Option

を使用しないメソッドに

Option

を認識しない関数を渡したい場合に便利です。


map

メソッドと同じように、**

lift

はnullを

None

にマップしません。

assertEquals(null, lifted.apply(Option.some(0)).get());

4. 2つの可能性のある結果を伴う計算に対して

Either

ご覧のとおり、

Option

クラスを使用すると、機能的な方法で値の欠如に対処できます。

ただし、「値なし」よりも多くの情報を返す必要がある場合があります。たとえば、正当な値またはエラーオブジェクトを返すことができます。


Either

クラスはそのユースケースをカバーします。


  • Either

    のインスタンスは

    Right

    または__Leftになることができますが、同時に両方にすることはできません

慣例により、右側は計算が成功した結果であり、左側は例外的な場合です。


4.1.

Either


を構築する

2つの静的ファクトリメソッドの1つを呼び出すことで

Either

インスタンスを取得できます。


Right



__を含む

Either

が必要な場合は、

right__を呼び出します。

Either<Integer, String> right = Either.right("value");

そうでなければ、

left

を呼び出します。

Either<Integer, String> left = Either.left(-1);

ここでは、計算は

String

または

Integer.

を返すことができます。


4.2.

Either


を使う


Either

インスタンスがあるとき、それが左か右かを確認し、それに従って行動することができます。

if (either.isRight()) {
    ...
}

さらに興味深いことに、機能的なスタイルを使って操作を連鎖させることができます。

either
  .map(String::toUpperCase)
  .getOrNull();


4.3. 予測


Option、Tryなどの他のモナドツールとの違いは、偏りがないことです。簡単に言うと、map()メソッドを呼び出した場合、

Either



Left



Right__のどちらの側で作業するのかわからないのです。

これが予測が役に立つところです。

  • 左と右の射影は、それぞれ左または右の値に焦点を合わせる

    Either

    の鏡面図です。

either.left()
  .map(x -> decodeSQLErrorCode(x));

上記のコードスニペットでは、

Either



Leftの場合、decodeSQLErrorCode()

が基になる要素に適用されます。

Either



Rightの場合、

それはしません。正しい投影法を使用しているときも同じです。

** 4.4. 効用メソッド

**


Options

と同様に、Fugueも

Eithers

のためのユーティリティでいっぱいのクラスを提供します、そしてそれはそのように呼ばれます:

Eithers

これには、

__Either

__のコレクションをフィルタリング、キャスト、および反復するためのメソッドが含まれています。

5.

Try

を使った例外処理

私たちはFugueでのthis-this-or-thatデータ型のツアーを

Try

という別のバリエーションで締めくくります。


Try



Either

と似ていますが、例外を扱うためのものです。


Option



Either

とは異なり、

Try

は単一の型でパラメータ化されています。これは、「その他」の型は

Exception

に固定されているためです(

Option

では暗黙のうちに

Void

)。

したがって、

Try



Success

または

Failure

のいずれかになります。

assertTrue(Try.failure(new Exception("Fail!")).isFailure());
assertTrue(Try.successful("OK").isSuccess());

** 5.1.

Try

のインスタンス化

**

多くの場合、成功または失敗として明示的に

Try

を作成することはありません。むしろ、メソッド呼び出しから作成します。


Checked.of

は指定された関数を呼び出し、その戻り値またはスローされた例外をカプセル化した

Try

を返します。

assertTrue(Checked.of(() -> "ok").isSuccess());
assertTrue(Checked.of(() -> { throw new Exception("ko"); }).isFailure());

もう1つのメソッド

Checked.lift

は、スローされる可能性のある関数を受け取り、それを

Try

を返す関数に

lift

します。

Checked.Function<String, Object, Exception> throwException = (String x) -> {
    throw new Exception(x);
};

assertTrue(Checked.lift(throwException).apply("ko").isFailure());

** 5.2.

Try

を使った作業

**


Try

を入手したら、最終的にそれを使用したいと思う3つの最も一般的なことがあります。

  1. その価値を引き出す

  2. 成功した値にいくつかの操作を連鎖する

  3. 関数で例外を処理する

そのうえ、明らかに、

Try

を破棄したり他のメソッドに渡したりする場合、上記の3つが唯一の選択肢ではありませんが、他のすべての組み込みメソッドはこれら3つのメソッドよりも便利です。

** 5.3. 成功する価値を引き出す+

**

値を抽出するには、

getOrElse

メソッドを使用します。

assertEquals(42, failedTry.getOrElse(() -> 42));

存在する場合は成功値、それ以外の場合は何らかの計算値を返します。


getOrThrow

などはありませんが、

getOrElse

は例外をキャッチしないため、簡単に記述できます。

someTry.getOrElse(() -> {
    throw new NoSuchElementException("Nothing to get");
});

** 5.4. 成功後の呼び出しの連鎖+

**

機能的スタイルでは、最初に明示的に抽出することなく、成功値(存在する場合)に関数を適用できます。

これは、

Option



Either

、その他のほとんどのコンテナおよびコレクションに見られる典型的な

map

メソッドです。

Try<Integer> aTry = Try.successful(42).map(x -> x + 1);

それは

Try

を返すので、我々はさらなる操作を連鎖することができる。

もちろん、

flatMap

もあります。

Try.successful(42).flatMap(x -> Try.successful(x + 1));

** 5.5. 例外からの回復

**

成功した値ではなく、

Try

(存在する場合)を除いて機能する類似のマッピング操作があります。

しかしながら、それらの方法は、それらの意味が例外から回復すること、すなわちデフォルトの場合に成功した

Try

** を生成することであるという点で異なる。

したがって、

recover

を使用して新しい値を生成できます。

Try<Object> recover = Try
  .failure(new Exception("boo!"))
  .recover((Exception e) -> e.getMessage() + " recovered.");

assertTrue(recover.isSuccess());
assertEquals("boo! recovered.", recover.getOrElse(() -> null));

ご覧のとおり、recovery関数は唯一の引数として例外を取ります。

リカバリー機能自体がスローした場合、結果は別の失敗

Try

です。

Try<Object> failure = Try.failure(new Exception("boo!")).recover(x -> {
    throw new RuntimeException(x);
});

assertTrue(failure.isFailure());


flatMap

に似たものは

recoverWith

と呼ばれます。

Try<Object> recover = Try
  .failure(new Exception("boo!"))
  .recoverWith((Exception e) -> Try.successful("recovered again!"));

assertTrue(recover.isSuccess());
assertEquals("recovered again!", recover.getOrElse(() -> null));

6.その他のユーティリティ

まとめる前に、Fugueの他のユーティリティのいくつかを簡単に見てみましょう。


6.1. ペア


Pair

は、Fugueが

left

および

right

と呼ぶ2つの同様に重要なコンポーネントで構成された、非常にシンプルで用途の広いデータ構造です。

Pair<Integer, String> pair = Pair.pair(1, "a");

assertEquals(1, (int) pair.left());
assertEquals("a", pair.right());

Fugueは

__Pair

__sには、マッピングや適切なファンクターパターン以外にも多くの組み込みメソッドを提供していません。

ただし、

__ Pair

__はライブラリ全体で使用されており、ユーザープログラムですぐに使用できます。

次の貧しい人によるLispの実装は、ほんの数回のキーストロークです。


6.2. 単位


Unit

は、「値なし」を表すことを意図した単一の値を持つenumです。

これはvoidの戻り値の型と

Void

クラスの代わりになり、

null

がなくなります。

Unit doSomething() {
    System.out.println("Hello! Side effect");
    return Unit();
}

しかし、驚くべきことに、**

Option



Unit

を理解していないため、値をnoneの代わりにある値のように扱います。

** 6.3. 静的ユーティリティ

**

作成してテストする必要のない静的なユーティリティメソッドが満載のクラスがいくつかあります。


Functions

クラスは、さまざまな方法で関数を使用および変換するメソッドを提供します。構成、アプリケーション、カリー化、

Option

を使用した部分関数、弱いメモ化など。


Suppliers

クラスは、同様の、しかしより限定された、

__Supplier

__のためのユーティリティのコレクション、すなわち引数のない関数を提供します。

最後に、

Iterables



Iterators

には、広く使用されているこれら2つの標準Javaインターフェースを操作するための多数の静的メソッドが含まれています。


7. 結論

この記事では、AtlassianによるFugueライブラリの概要を説明しました。


Monoid



Semigroups

のような代数的に重いクラスは、一般論者の記事には収まらないので、触れていません。

しかし、それらについてはFugue

javadocs

およびhttps://bitbucket.org/atlassian/fugueで読むことができます。/src[ソースコード]

また、GuavaやScalaとの統合など、オプションのモジュールについても触れていません。

これらすべての例とコードスニペットの実装はhttps://github.com/eugenp/tutorials/tree/master/libraries[GitHubプロジェクト]で見つけることができます – これはMavenプロジェクトです。そのまま実行します。