2012-09-03 4 views
19

私はプレイを持っています! 2外部サービスからJSON形式のデータを取得する必要があるScalaアプリケーションの場合モナド変圧器はサービスからJSONを取得するために適用されますか?

The Play!フレームワークはPromiseで応答をラップすることによって非同期的にHTTP要求を行うことを可能にします。 Promiseは将来利用できる値をラップするモナドです。

これは問題ありませんが、私の場合、Webサービスから得られるものはJSON文字列です。私はそれを解析する必要があり、解析が失敗する可能性があります。だから私はOptionに入るものをすべてラップする必要があります。その結果、私のメソッドの多くはPromise[Option[Whatever]]を返しています。つまり、タイプWhateverの値は、おそらく後で利用可能になります。

私はそのような値を超えて操作しなければならないときはいつでも、それはmapに2回必要です。 mapのような関連するメソッドを定義Promise[Option[A]]

  • をラップしている、Hope[A]を言う、新しいタイプを作成

    • (または多分私はforeachを使用して、いくつかのコレクションから継承する必要があります:私は、次の方法でこれを扱うことを考えていました。形質)?flatten
    • Promise[Option[A]]Hope[A]の間の暗黙的な変換を提供します。

    mapを定義することは容易である - 二つのファンクタの組成物は、再びファンクタであり - そしてflattenは、この場合には、明示的に行うことができ、又はOptionとのモナドを構成するたびに。

    しかし、私はこのことを再考する必要はないと私の限られた理解です:この場合、モナドトランスがあります。モナド変圧器を一度も使用していないと思います。これは質問のポイントです。

    この状況でモナドトランスフォーマーを使用できますか?実際にそれらを使ってどうやって行くのですか?

  • 答えて

    15

    をScalazライブラリのOptionT変圧器を使用して、次のことができるようにすべきですタイプPromise[Option[A]]の値をタイプOptionT[Promise, A]の値に変換します。 Scalaz 7使用

    :この値を使用するには

    import scalaz.OptionT._ 
    val x: OptionT[Promise, Int] = optionT(Promise.pure(Some(123))) 
    

    は、それにmapflatMapを呼び出すたとえば、あなたがPromisemapためFunctorflatMapMonad)のための適切な型クラスを提供する必要があります。

    Promiseはモナディックなので、Monad[Promise]のインスタンスを指定することは可能です。 (型クラスは、継承階層を形成するので、あなたは、無料でFunctorApplicativeを取得します。)たとえば、(注意:私はこれをテストしていませんでした!):

    implicit val promiseMonad = new Monad[Promise] { 
        def point[A](a: => A): Promise[A] = Promise.pure(a) 
        def bind[A, B](fa: Promise[A])(f: A => Promise[B]): Promise[B] = fa flatMap f 
    } 
    

    簡単な例として、あなたが今使用することができますrunメソッドを呼び出して、OptionT[Promise, A]から基礎となるPromise[Option[A]]値を取得するには

    def foo[A, B](x: OptionT[Promise, A], f: A => B): OptionT[Promise, B] = x map f 
    

    :内部の値にタイプA => Bの機能を適用するOptionT[Promise, A]map、。

    def bar[A, B](x: Promise[Option[A]], f: A => B): Promise[Option[B]] = 
        optionT(x).map(f).run 
    

    あなたは互換性のあるタイプのいくつかの操作を構成することができたときに、モナド変圧器を使用して動作間のOptionT[Promise, _]タイプを保存し、最後に根本的な値を取得するからより多くの利益を得ることができます。

    補足の操作を構成するには、タイプA => OptionT[Promise, B]の関数が必要です。

    +0

    ありがとう、これは私が必要とするものです! – Andrea

    +0

    私はそれを試して、すべて私のアプリケーションで正常に動作します。私の関数の戻り値の型を簡素化するために型の別名 'type Hope [A] = Promise [Option [A]]を導入すると、コンパイル時エラー' java.lang.IllegalArgumentException :転置するには、すべてのコレクションのサイズが同じであることが必要です。なぜあなたは何か手がかりがありますか? – Andrea

    +0

    @アンドレア:明示的な型パラメータを 'optionT'で使用すると、エラーはなくなります。コンパイラのバグのように見えますが、時間がある場合は、[Issue Tracker](https://issues.scala-lang.org/secure/Dashboard.jspa)をチェックするか、ここでフォローアップの質問をする価値があります。 –

    1

    - 削除 -

    編集:

    さて、あなたは、単にここscalaz.OptionTを使用することができます。

    val x = optionT(Promise { /* api call */ some("""{ "foo": "bar" }""") }) 
    val mapped = x.map(Json.parse).run // run return the resulting Promise[Option[T]] 
    
    +0

    4行ではなく 'x map(_ * 2)'と書いています。さて、この小さな例ではほとんど価値がないかもしれませんが、より複雑なケースでは、問題がより明確になるかもしれないと思います。 – Andrea

    +0

    updated私の答え – drexin

    +0

    'x'と' opt'が互換性のない型であるため、このfor-comprehensionはコンパイルされません。 2つの補題を入れ子にする必要があります。 –

    関連する問題