2016-05-04 5 views
3

さて、私は長年のOO開発者ですが、これは "newfound"という機能プログラミングの世界に入り込んでいます。これに関連して、私はnullthrowのようにコーディングしようとしています。 Javaの土地にはOptionEitherのモナドで遊んでいます。 (少なくとも、私はと考えています。彼らはモナドです;私はまだその用語に慣れていませんし、私が正しく使用しているかどうかは...)私はAtlassian Fugueライブラリから出ます。 Functional Javaライブラリーを調べてみましたが、それは私が現時点で準備しているよりはるかに大きくなっています。もしfjが私の必要とすることを実行し、フーガはしない、私はそれのためにすべてです。Fugue/FunctionalJavaを使用してNullからThrowするには?

基本的に、私が達成しようとしていることはこのかなり醜いJavaコードと同等です:Eitherモナドを使用して、

InputObject input = blah(); 

try { 
    final String message = getMessage(input); 

    if (message != null) { 
     try { 
      final ProcessedMessage processedMessage = processMessage(message); 
      if (processedMessage != null) { 
       try { 
        final ProcessedDetails details = getDetails(notificationDetails); 
        if (details != null) { 
         try { 
          awesomeStuff(details); 
         } catch (AwesomeStuffException ase) { 
          doSomethingWithAwesomeStuffException(ase); 
         } 
        } 
       } catch (GetDetailsException gde) { 
        doSomethingWithGetDetailsException(gde); 
       } 
      } 
     } catch (ProcessMessageException pme) { 
      doSomethingWithProcessMessageException(pme); 
     } 
    } 
} catch (GetMessageException gme) { 
    doSomethingWithGetMessageException(gme); 
} 

単純に、私はこのような何かを行うことができるように期待していた。

getMessage(input).fold(this::doSomethingWithGetMessageException, this::processMessage) 
       .fold(this::doSomethingWithProcessMessageException, this::getDetails) 
       .fold(this::doSomethingWithGetDetailsException, this::awesomeStuff) 
       .foldLeft(this::doSomethingWithAwesomeStuffException); 

...ここで、ロジックishの各メソッドは、適切な例外(恐らくドメイン固有のエラークラスですが、例外は今のところ十分です)を左に、そしてOption<something>をリグht。 doSomethingWith...メソッドは、エラーに対してどのようなログ/処理を行ってもかまいません。論理的方法の例は、次のようになります。

Either<GetMessageException, Option<String>> getMessage(final InputObject input) { 
    if (input == null) { 
    return Either.left(new GetMessageException("Input was null"); 
    } 

    try { 
    return Either.right(loadMessage(input)); 
    } catch (Exception ex) { 
    return Either.left(new GetMessageException(ex)); 
    } 
} 

他の方法も同様に定義されます。好ましくはではなくで、パラメータとしてOptionを取ります。以前のメソッドがOption.noneを返した場合、このメソッドは単に呼び出されません。

私が実際にこれをテストするのを妨げたジェネリック型のコンパイラエラーのネストを除いて、論理的には、私がとにかくほしいと思うようには見えません。最初のEither.left値が返された後でopを返しますが、もっと見ると、Either.leftの結果を次の呼び出しに渡してさらに続行しようとしていると推測しています。

私はwas種類のオプションを使用して、ちょうど私の目標を達成することができ:

ProcessedMessage processMessage(final String message) { 
    try { 
    return doProcessing(message); 
    } catch (Exception ex) { 
    final GetMessageException gme = new GetMessageException(ex); 
    doSomethingWithGetMessageException(gme); 
    return null; 
    } 
} 
:元のブロックと同じロジックを達成するために、残念ながら

getMessage(input).map(this::processMessage) 
       .map(this::getDetails) 
       .map(this::awesomeStuff); 

を、ロジック・メソッドはこれに同様に実現されるだろう

は結果をOptionに持ち上げますので、ここでnullを返しても問題ありません。残念ながら、私は依然として呼び出し元からのエラー状態を隠しています(さらに悪いIMOの場合はnullを使用してエラーを示しています)、doProcessingが潜在的に妥当な理由でnullを返すかどうかは何も言いません。

だから、基本的に、私は以前のgetMessage例の線に沿ってメソッドのシグネチャを持つようにようをいただきたい -

Either<GetMessageException, Option<String>> getMessage(final InputObject input) 

私は、戻り値を確認することができるという考えが好きで、時を知っています一見 "この方法は失敗するか、そこにあるかもしれないし、そうでないかもしれない何かを返すでしょう。「しかし、私はFPにあまりにも新たなんだとどのようにこれについて行くには見当もつかない。

そして、私の理解を(おそらく欠陥はあるが)、私は(ただし

getMessage(input).fold(this::doSomethingWithGetMessageException, this::processMessage) 
       .fold(this::doSomethingWithProcessMessageException, this::getDetails) 
       .fold(this::doSomethingWithGetDetailsException, this::awesomeStuff) 
       .foldLeft(this::doSomethingWithAwesomeStuffException); 

に似た何かを行うことができるということですすなわち

  • がEither.rightのEX限り、全体のチェーンを介して継続)適切なdoSomethingWithXExceptionメソッドを呼び出して、停止(Either.left後の任意の時点で

    1. 停止処理が処理されること)は、異なる方法と可能性ists(関連する関数がオプションを返す場合はOption.none()ではありません)。
    2. awesomeStuffへの呼び出しが失敗した場合は、doSomethingWithAwesomeStuffExceptionとコールしてください。他の方法が失敗した場合は、届きません。

    私は妥当でさえしようとしていますか?私は、いくつかの可能性はあると確信していますが、これをJavaの構文に挑戦するのはあまりにも複雑で、たとえそれらのライブラリ(または私が気づいていない他のライブラリ)を使用していてもかまいません。

  • +0

    私はこのコードが最初は奇妙だと思います。ヌル結果と例外を混在させています.2つの異なるアプローチを扱うのは非常に難しいです。そしてif-conditionを逆にすると、ネストされたブロックを避けることができます。コードを読みやすくします。それとも単なる学問的な例ですか? – kan

    +1

    ところで、トライモードがあります。https://github.com/jasongoodwin/better-java-monads/blob/master/src/main/java/com/jasongoodwin/monads/Try.java – kan

    +0

    ほとんど学業だけです。私はモナド様式のコードを書こうとし始めましたが、それがうまくいかなかったときに、「普通の」Javaで実装する方法を簡単に書きました。 「実世界」のJavaでは、例外をログに記録して人生を進む単一のキャッチブロックがあると思います。 – MCory

    答えて

    1

    これは確かに意見のある質問です。あなたが望むのはflatMapで、モナドのバインド演算子とも呼ばれます。残念ながら、すべての機能ライブラリがこれを行うわけではありません(つまり、GuavaはオプションでflatMapを持っていません)。 RxJavaReactorはこのタイプの操作をサポートしていますが、それはあなたのサービスをよりストリームにする必要があります(私はこれを強く推奨します)。

    Javaでこの種のロジックを実行することには多くの問題があります。つまり、Javaにはパターンマッチングがありません。variants/ADTs/case classesです。そのため上記の

    最もシリアライザは、ジェネリックコンテナ(すなわちジャクソン)の問題を持っているので、ほとんどの時間は良いアプローチは、結果(通常は静的インラインクラス)を返すように行動に無料不変のユニークなクラスを使用することです。

    私はこのサービスを常に行っています。これは、特定のサービス要求に固有のインライン静的クラスを作成することです。インラインクラス間でアルゴリズムの振る舞いを再利用するためのインターフェースを実装するかもしれませんが、現実には、Javaはタプル/バリアント/ ADT /ケースクラス(前を参照)のためのひどいサポートを持っています。

    多くの場合、データが失われる例外をスローするよりも、これは確かに優れています(必ずしもそうとは限りません)。

    +0

    私は最近の旅行でフラットマップ/バインドを見つけました。私の脳はほとんどそれを理解したいと思いますが、マップとの違いは分かりますが、ライトが消える直前にシャットダウンします。 fugueライブラリのEitherクラス*は、flatMapメソッドを持っています。 「ある場合は右辺値に渡って与えられた関数を束縛する」と言う。チェーンの次の機能に移動する前に、両方のケース(左右)を処理しようとしているので折りたたんで行ったので、flatMapは機能しませんでした。私はそれを考えて間違っていますか? – MCory

    +0

    スペースがなくなりましたが、尋ねるつもりでした。フラットマップで何を示唆しているのか、何らかの人為的な例を与えることができますか?可能であれば、複数のEitherベースのメソッドとOption値との連鎖。レスポンス情報を処理するコンテナクラス(障害情報やデータがある場合)を処理する方法は間違いなくありましたが、変更のための機能をモナドな方法で試してみたいと思っていました。 – MCory

    +1

    これは間違っているかもしれない長い批判であなたのコメントに答えるのは難しいですが、その欠点は、flatMapが*コンテナ*中にあるものを変形することができることです。これはJavaでは些細なことですが、並行性と抽象的なさまざまな実行になると、これは大量の配当で成果を上げます。私はちょうど致命的なflatMapと友人が(特に飲み物のカップルの後:) :)を説明するのに十分スマートではない)。 –

    関連する問題