2016-03-28 15 views
0

私はスカラー初心者です。私はJavaの背景から来た。私はモナドを読んでいて、それについての一般的な考え方をしています。 Listのようなタイプの操作でmapflatMapの操作を理解することはできますが、reader monadになると、私の頭の中には私の頭がラップできません。 誰か簡単な例を挙げてください。ReaderMonadのマップとフラットマップ操作の意味

私はfor-comprehensionsのような派手な構文を使用できるように単項関数の構成を容易にするためにReaderMonadsが必要であると理解しています。私は、それが起こるためには、モナドの神が必要であることを理解しています。 私が理解したいのは、「マップとフラットマップは何を意味するのですか」は機能の意味ですか?

答えて

4

多くの場合、Reader[A, B]と書かれた読者モナドは、機能タイプA => Bです。

mapはそうのような pureflatMapの観点で実施することができる
trait Monad[M[_]] { 
    def pure[A](a: A): M[A] 
    def flatMap[A, B](ma: M[A])(f: A => M[B]): M[B] 
} 

def map[A, B](ma: M[A])(f: A => B): M[B] = flatMap(ma)(a => pure(f(a))) 

私たちが行う必要があるので、まず最初に私たちのバイナリ型を作ることであるScalaではモナドのエンコーディングは次のようになりますコンストラクタReaderは、Monadが期待する単項型コンストラクタに適合します。これは、第1の(入力)タイプのパラメータを固定し、第2の(出力)タイプのパラメータを空のままにすることによって行われる。

implicit def readerMonad[X]: Monad[X => ?] = ??? 

(ここでは?の使用は素晴らしいkind-projectorコンパイラプラグイン経由で)。

pureで始まり、M[_]の出現をX => _に置き換えます。タイプAのいくつかの値が与えられ

def pure[A](a: A): X => A 

、我々はタイプXの値を指定した関数を返す必要があり、タイプAの値を返します。これが可能なのは定数関数だけです。

flatMapを代入
def pure[A](a: A): X => A = _ => a 

..

def flatMap[A, B](ma: X => A)(f: A => (X => B)): X => B = (x: X) => ??? 

これは少しトリッキーですが、我々は、実装に私たちを導くために型を使用することができます!我々は:

ma: X => A 
f: A => (X => B) 
x: X 

Bを取得したいと思います。私たちがそれを行う方法を知っている唯一の方法は、AXが欲しいfです。正確にはXからxまでですが、Aが必要です。我々はをmaから得ることができ、それはXを望んでおり、再びxだけが私たちを提供していることがわかります。そこで我々は、XReaderflatMapは、「環境」(または「設定」)のうちのいくつかの値Aを読むために言う..

声を出して読む
def flatMap[A, B](ma: X => A)(f: A => (X => B)): X => B = 
    (x: X) => { 
    val a = ma(x) 
    val b = f(a)(x) 
    b 
    } 

を持っています。次に、Aで分岐し、別の値Bを環境から読み取ります。

我々は先に行くと同様、手動mapを実装することができます

def map[A, B](ma: X => A)(f: A => B): X => B = ??? 

が期待される出力 X => Bで、引数 X => AA => Bを見ると、これはまさに確かにそれはある関数合成、のように見えます。輸入品との catsを使用して

使用例:

case class Config(inDev: Boolean, devValue: Int, liveValue: Int) 

私たちは「DEV」環境にいるかどうかを教えてくれるし、提供します:

import cats.data.Reader 

のは、我々はいくつかのConfigタイプを持っているとしましょう私たちは "dev"と "live"のために何かを評価しています。まず、inDevフラグを読み取った簡単なReader[Config, Boolean]を書き始めることができます。

val inDev: Reader[Config, Boolean] = Reader((c: Config) => c.inDev) 

そして、ブール値を与えて適切な値を読み取るプレーン関数を書くことができます。

def branch(flag: Boolean): Reader[Config, Int] = 
    if (flag) Reader((c: Config) => c.devValue) 
    else  Reader((c: Config) => c.liveValue) 

今、私たちは、2を構成することができます。

val getValue: Reader[Config, Int] = 
    for { 
    flag <- inDev 
    value <- branch(flag) 
    } yield value 

そして今、我々は、様々なConfig値を渡すことによって、私たちIntを取得することができます:一般的に

getValue.run(Config(true, 1, 2)) // 1 
getValue.run(Config(false, 1, 2)) // 2 

これは、として使用することができます依存関係注入を実現する素敵な方法です。

+0

答えのためのThx。非常に深いです。私はこれを何度も読んで、私が完全に理解しているかどうかを確認する必要があります。あなたはまた私に使い方の簡単な例を教えてくれますか? –

+0

例を含めて私の答えを編集しました:-) – adelbertc

関連する問題