2016-10-01 7 views
2

私はScalaでより機能的なコードを書く方法を学ぼうとしていますが、モナド構造から値を引き出すのではなく、マップ/フラットマップなどを使って値を操作するのは難しいです。これは単一のモナドで作業するときは簡単ですが、以下のように構造体を操作するためにこれをスケールするにはどうすればよいですか?モナド構造が深くネストされている場合、どのようにモナディ構造を使用するのですか?

たとえば、値をJsResultの内部に変換する慣習的な方法はありますか?

Option[Future[JsResult[LoginResponse]]] 
+1

答えは、それぞれのレベルが何を意味しているのか、そしてこれらのことで何をするのかにある程度依存します。もっと知らないと、 'Future'と' JsResult'を単一の 'Future'レベルに平坦化し、' Future'の中で 'Option'を動かし、(CatsかScalazの依存関係ならば)その結果を 'OptionT [Future、LoginResponse]'に変換します。 –

+0

a)http://eed3si9n.com/learning-scalaz/Monad+transformers.html b)http://atnos-org.github.io/eff-cats/ – Reactormonk

+0

@Reactormonk Monadトランスフォーマーはアイデアかもしれません。ポスターが彼の例としてどのように使用できるかについてのサンプルコードがあります:Option [Future [JsResult [LoginResponse]]]これは素晴らしいものです。私は答えとして私の最善の努力をしました - しかし、それが良いものがあるかどうかを見ることは良いでしょう –

答えて

0

異なるタイプのネストされたモナドは、モナドが同じタイプであることを理解するために、扱いにくいことがあります。あなたは行うことができます次のいずれかの

val mappedValue = for (fut <- deepMonad) yield { 
    for (opt <- fut) yield { 
    for (bool <- opt) yield { 
     //some logic 
    }  
    } 
} 

またはあなたがあなたのためにそれを離れて隠しutilのを作ることができるためにネストされたの

たくさん。

プロジェクトで一般的に使用する特定の構造を見ていて、純粋なScalaを使いたい場合は、以下のようにマップ/ foreachを行うことができます。

など。

object MyUtils { 

    implicit class MyWrapper[A](deepMonad: Option[Future[Option[A]]]) { 

    def fmap[B](f: A => B) = { 
     for (fut: Future[Option[A]] <- deepMonad) yield { 
     for (opt: Option[A] <- fut) yield { 
      for (b: A <- opt) yield { 
      f(b) 
      } 
     } 
     } 
    } 

    def myForeach[U](f: A => U): Unit = { 
     for (future <- deepMonad) { 
     for (opt <- future) { 
      for (b <- opt) { 
      f(b) 
      } 
     } 
     } 
    } 
    } 

} 

object Test extends App { 

    import MyUtils._ 

    val deepMonadExample:Option[Future[Option[Boolean]]] = Some(Future.successful(Some(true))) 

    val x: Option[Future[Option[String]]] = deepMonadExample.fmap { 
    case v:Boolean => "Result: "+v 
    } 
    x.myForeach{ v => println(v) } 

} 

Scalazを使用したい場合は、Monadクラスを使用してより汎用的なユーティリティを作成できます。 Scalazには、オプション、未来、そして他のものを箱の中で動かすような、あらかじめ組み込まれた意味があります。しかし、JsResultのようなクラスにはscalaz Monadのインスタンスがないので、それを作る必要があります。

など。

import play.api.libs.json.{JsSuccess, JsError, JsResult} 

import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.Future 
import scalaz.Monad 

//this creates the fmap method 
object HandleDeepMonads { 

    import scala.language.higherKinds 
    import scalaz.Monad 
    import scalaz.Scalaz._ 

    implicit class ThreeMonadMap[M[_] : Monad, M2[_] : Monad, M3[_] : Monad, A](v: M[M2[M3[A]]]) { 
    def fmap[B](f: A => B): M[M2[M3[B]]] = { 
     for (a <- v) yield 
     for (b <- a) yield 
      for (c <- b) yield f(c) 
    } 
    } 

} 

//Since JsResult has no native Monad in scalaz - you can make your own one 
object MyCustomMonads { 

    implicit object JsResultMonad extends Monad[JsResult] { 
    def point[A](a: => A): JsResult[A] = JsSuccess(a) 

    def bind[A, B](fa: JsResult[A])(f: A => JsResult[B]): JsResult[B] = fa match { 
     case JsSuccess(v, _) => f(v) 
     case [email protected](_) => e 
    } 
    } 

} 

object Test extends App { 

    import HandleDeepMonads._ 
    import MyCustomMonads._ 
    import scala.language.higherKinds 
    import scalaz.Scalaz._ 

    val deepMonadExample: Option[Future[JsResult[String]]] = Some(Future.successful(JsSuccess("Hello"))) 
    val deepMonadExample2: Option[Future[JsResult[Boolean]]] = Some(Future.successful(JsError(Nil))) 
    val deepMonadExample3: Option[Future[Option[Boolean]]] = Some(Future.successful(Some(true))) 
    val deepMonadExample4: Option[Future[JsResult[Boolean]]] = None 

    // Some(successful(JsSuccess("Result: true"))) 
    val x = deepMonadExample.fmap { 
    "Result: " + _ 
    } 
    // Some(successful(JsError())) 
    val x3 = deepMonadExample3.fmap { 
    "Result: " + _ 
    } 
    // Some(successful(Some("Result: Hello"))) 
    val x2 = deepMonadExample2.fmap { 
    "Result: " + _ 
    } 
    // None 
    val x4 = deepMonadExample4.fmap { 
    "Result: " + _ 
    } 
} 

あなたが深い2に、あなたのモナドを簡素化することができれば、あなたが(コメントで示唆されているように、例えばOptionT)をScalazの株式標準モナド変圧器を使用することができます。私は彼らが2つの深さで非常によく働いているのを見ましたが、私はもっとネストした状態でそれらを使ったことはありません。

関連する問題