多くの場合、Reader[A, B]
と書かれた読者モナドは、機能タイプA => B
です。
map
はそうのような
pure
と
flatMap
の観点で実施することができる
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
を取得したいと思います。私たちがそれを行う方法を知っている唯一の方法は、A
とX
が欲しいf
です。正確にはX
からx
までですが、A
が必要です。我々はをma
から得ることができ、それはX
を望んでおり、再びx
だけが私たちを提供していることがわかります。そこで我々は、X
Reader
のflatMap
は、「環境」(または「設定」)のうちのいくつかの値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 => A
と
A => 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
これは、として使用することができます依存関係注入を実現する素敵な方法です。
答えのためのThx。非常に深いです。私はこれを何度も読んで、私が完全に理解しているかどうかを確認する必要があります。あなたはまた私に使い方の簡単な例を教えてくれますか? –
例を含めて私の答えを編集しました:-) – adelbertc