5

F [G []オブジェクトに機能:Scalazモナドトランス。適用F1:A => G [B]、F2:B => G [C] Iは、2つ(またはそれ以上)の関数として定義した

val functionM: String => Option[Int] = s => Some(s.length) 
val functionM2: Int => Option[String] = i => Some(i.toString) 

私は李ない

data.map(_.flatMap(functionM).flatMap(functionM2)) 
res0: List[Option[String]] = List(Some(3)) 

:私の質問は以下のような結果を得るために(良い方法で)機能を構成する方法です

val data: List[Option[String]] = List(Option("abc")) 

:私はのように定義されているいくつかのデータをも持っています上記の関数呼び出しの構文を使用してください。私はこのような多くの場所がある場合、コードは非常に判読不能です。

私はOptionT scalazのモナド変換子と遊ぶことを試みたが、それはまだマップを入れ子にしても同様のネストされたオプションを生成しています

OptionT(data).map(a => functionM(a).map(functionM2)).run 
res2: List[Option[Option[Option[String]]]] = List(Some(Some(Some(3)))) 

私は何を達成したいことは、多かれ少なかれ、このようなものです:

Something(data).map(functionM).map(functionM2) 

またはより良い:

val functions = functionM andThenSomething functionM2 
Something(data).map(functions) 

試してみるといいですね。私はscalazがTryTモナドトランスを持っていないことを知っているので、Tryで動作する関数をうまく構成する方法はありますか?

+0

'Kleisli(functionM)> => Kleisli(functionM2)'を使って 'val function = functionM andThenSomething functionM2'を達成できますが、これで次に何をするのか分かりません... –

答えて

7

Łukaszが言及しているように、ここではKleisliが最も重要です。いつでもあなたは形状A => F[B]の一部の機能を持っていて、彼らは通常の関数A => Bであるかのようにそれらを構成したい(とあなたがFためflatMapを持っている)、あなたはKleisli矢印などの機能を表すことができます。

import scalaz._, Scalaz._ 

val f1: Kleisli[Option, String, Int] = Kleisli(s => Some(s.length)) 
val f2: Kleisli[Option, Int, String] = Kleisli(i => Some(i.toString)) 

そして、その後:あなたがリーダーモナドの考え方に精通している場合、Kleisliだけのアイデアをフレーミングもう少し一般的な方法です-it ReaderTとまったく同じものがある

scala> f1.andThen(f2).run("test") 
res0: Option[String] = Some(4) 

(詳細は私の答えhereを見ます)。

この場合、List[Option[A]]内のすべての部分に届かず、Aで直接作業する必要がないため、モナールトランスが探しているとは思われません.2つのレベルを区別しています。

scala> val data: List[Option[String]] = List(Option("abc")) 
data: List[Option[String]] = List(Some(abc)) 

scala> data.map(_.flatMap(f1.andThen(f2))) 
res1: List[Option[String]] = List(Some(3)) 

最後に、ScalazはMonad(あなたがここに必要があると思い何であるかBind、)提供していないという理由だけで、インスタンス:f1と上記f2の定義を考えると、私はおそらくちょうど次のように記述したいですTryの場合は、自分で書くことはできません。たとえば、次のように

import scala.util.{ Success, Try } 

implicit val bindTry: Bind[Try] = new Bind[Try] { 
    def map[A, B](fa: Try[A])(f: A => B): Try[B] = fa.map(f) 
    def bind[A, B](fa: Try[A])(f: A => Try[B]): Try[B] = fa.flatMap(f) 
} 

val f1: Kleisli[Try, String, Int] = Kleisli(s => Success(s.length)) 
val f2: Kleisli[Try, Int, String] = Kleisli(i => Success(i.toString)) 

そして:

scala> val data: List[Try[String]] = List(Try("abc")) 
data: List[scala.util.Try[String]] = List(Success(abc)) 

scala> data.map(_.flatMap(f1.andThen(f2))) 
res5: List[scala.util.Try[String]] = List(Success(3)) 

一部の人々は、例外の存在下でTryため、このようなFunctorまたはMonadまたはBindインスタンスの合法性に関するいくつかの懸念を持っており、これらの人々は傾向があります私はそれが気にすることが難しいと思う(私の見解では、は完全にTryを避ける)。

+0

Kleisli 。ネストされたマップの使用を避けたい場合は、OptionTトランスを使用できます。 インポートscalaz._、Scalaz._ ヴァルF1:Kleisli [オプション、文字列、INT] = Kleisli(S =>一部(s.length)) ヴァルF2:Kleisli [オプション、INT、文字列] = Kleisli val関数:Kleisli [オプション、文字列、文字列](0 => Some(i.toString)) val f3:Kleisli [オプション、文字列、文字列] = Kleisli(s = "Some(s +" hello " ] = f1 andThen f2 andThen f3 valデータ:リスト[Option [String]] =リスト(Option( "abc")) OptionT.optionT(データ).map(関数).run.flatten – NieMaszNic

関連する問題