そのため、モナドはパラメトリックデータ型であり、必然的に2つの操作を実装します:モナド(文献ではunit関数)とflatMap()関数(文献ではbindと呼ばれることもあります)を作成し、いくつかのルールに従います。 これらは、計算リンク戦略を実装するために使用されます。 最も単純なモナドの例を次に示します。
trait Monad[T] { def flatMap[U](f: T => Monad[U]): Monad[U] } def unit[T](x: T): Monad[T]
flatMap
関数は、入力として、モナド(モナドはコンテナです)にあるデータを受け入れる関数を取り、新しいモナドを返します。 後で示すように、関数は異なるタイプのモナド(TではなくU)を返すことができることに注意してください-これは非常に便利です。
unit
関数に関しては、モナドを作成する責任があり、モナドごとに異なります。 たとえば、ユニット関数。
Option Some(x)
List List(x)
Try Success(x)
各モナドに対して、
map
関数を定義し、
flatMap
と
unit
組み合わせで表現できます。 例:
def mapExample() { val monad: Option[Int] = Some(5) assert(monad.map(squareFunction) == monad.flatMap(x => Some(squareFunction(x)))) }
また、各モナドは3つの法律に従う必要があり、モナド合成が予測可能な方法で機能することを保証する必要があります。 これらの法則をモナドオプションで確認します。
まず、検証に使用する2つの単純な関数を定義します。これらは2乗と増分であり、Optionを返します。これは、flatMapに転送し、さらに合成するために行われます。
def squareFunction(x: Int): Option[Int] = Some(x * x) def incrementFunction(x: Int): Option[Int] = Some(x + 1)
最初の法則は
Left unit law
と呼ばれ、次のようになります。
unit(x) flatMap f == f(x)
そして、正の値を持つタイプ(オプションの場合はSome)にflatMap関数を適用し、そこに何らかの関数を渡すと、結果はこの関数を変数に単純に適用するのと同じになると言います。 これは、以下のコードでより良く示されています。
def leftUnitLaw() { val x = 5 val monad: Option[Int] = Some(x) val result = monad.flatMap(squareFunction) == squareFunction(x) println(result) }
予想どおり、結果は
true
になり
true
。
2番目の法則は
Right unit law
と呼ばれ、次のようになります。
monad flatMap unit == monad
そして、データ(モナドにあるもの)からモナドを作成する関数をflatMapに渡すと、出力で同じモナドが得られると彼は言います。
def rightUnitLaw() { val x = 5 val monad: Option[Int] = Some(x) val result = monad.flatMap(x => Some(x)) == monad println(result) }
flatMap関数はモナドを展開し、
x
を取り出して、新しいモナドを構築する関数
x => Some(x)
渡します。
flatMap
変数
monad
None
に設定されている場合、
flatMap
は単に
None
返すだけで、渡された関数を呼び出さないため、結果はいずれにしても
true
になります。
3番目の法則は結合法と呼ばれます:
(monad flatMap f) flatMap g == monad flatMap(x => f(x) flatMap g)
Scalaに書き込む場合:
def associativityLaw() { val x = 5 val monad: Option[Int] = Some(x) val left = monad flatMap squareFunction flatMap incrementFunction val right = monad flatMap (x => squareFunction(x) flatMap incrementFunction) assert(left == right) }
そして、この法律のこの順守は、私たちに通常の形で、つまり、次の代わりに
for comprehension
に使用する権利を与えます:
for (square <- for (x <- monad; sq <- squareFunction(x)) yield sq; result <- incrementFunction(square)) yield result
私たちは書くことができます:
for (x <- monad; square <- squareFunction(x); result <- incrementFunction(square)) yield result
したがって、これらのすべての法則は、 Wikipediaを信じている場合、計算チェーンのロジックをカプセル化できるという事実を示しています。これはまさにモナドの目的です。 これは、
Future
モナドとアクターを適用すると非常に明確に見られますが、これは別の記事のトピックです。 計算の連鎖を示すために、サーバーのポートとホストを計算するための2つの単純な関数を作成し、それらを記述して肯定的な
Some
結果を返します。 そして、これらの関数の結果に応じた
InetSocketAddress
の作成。
def findPort(): Option[Int] = Some(22) def findHost(): Option[String] = Some("my.host.com") val address: Option[InetSocketAddress] = for { host <- findHost() port <- findPort() } yield new InetSocketAddress(host, port) println(address)
このコードの実行結果は次のようになります:
Some(my.host.com/82.98.86.171:22)
。
yield
は
Option
を返し、それをさらに計算するために使用することに注意してください。 アドレス自体を取得するために、
map
関数を使用して結果を表示します;計算チェーン内の関数のいずれかが
None
返す場合、一般的な結果も
None
ます。
address.map(add => println("Address : " + add)).getOrElse(println("Error")) // Address : my.host.com/82.98.86.171:22
モナドを実際に使用するには、まず
flatMap
と
map
が負の入力で実行されないことを覚えておく必要があります(
Option
これは
None
)。 これらの機能を使用すると、エラーとの戦いが大幅に簡素化されます。