Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

モナドについて調べていく(15)

One Div Zero: Monads are Elephants Part 2の翻訳続き。

Filtering "For"(「for」のフィルタリング)

So far our monads have built on a few key concepts. These three methods - map, flatMap, and forEach - allow almost all of what "for" can do.

これまでモナドはいくつかのキーとなるコンセプトで構築してきました。これら3つのメソッド、map、flatMap、forEachは「for」が実行できることのほとんどすべてを許可します。


Scala's "for" statement has one more feature: "if" guards. As an example

Scalaの「for」ステートメントはもう1つ機能があります。「if」ガードです。例を示します。

val names = List("Abe", "Beth", "Bob", "Mary")
val bNames = for (bName <- names;
   if bName(0) == 'B'
) yield bName + " is a name starting with B"

assert(bNames == List(
   "Beth is a name starting with B", 
   "Bob is a name starting with B"))

"if" guards get mapped to a method called filter. Filter takes a predicate function (a function that takes on argument and returns true or false) and creates a new monad without the elements that don't match the predicate. The for statement above gets translated into something like the following.

「if」ガードはfilterと呼ばれるメソッドにマップされます。filterは述語関数(引数を取りtrueかfalseを返す関数)を引数に取り、述語にマッチしない要素を除いた新しいモナドを作ります。
上記のforステートメントは次のようなものに変換されます。

val bNames = 
   (names filter { bName => bName(0) == 'B' })
   .map { bName => 
      bName + " is a name starting with B" 
   }

First the list is filtered for names that start with B. Then that filtered list is mapped using a function that appends " is a name..."

最初にリストが名前がBで始まるものだけを残すフィルターにかけられます。それからそのフィルターにかけられたリストは" is a name..."を追加する関数を使ってマップされます。


Not all monads can be filtered. Using the container analogy, filtering might remove all elements and some containers can't be empty. For such monads you don't need to create a filter method. Scala won't complain as long as you don't use an "if" guard in a "for" expression.

すべてのモナドがフィルターできるわけではありません。コンテナの比喩を使うと、フィルターにかけることはすべての要素を削除するかもしれないですし、空にできないコンテナもあります。
そのようなモナドではfilterメソッドを作成する必要はありません。Scalaは「for」式で「if」ガードを使わない限りエラーとしません。


I'll have more to say about filter, how to define it in purely monadic terms, and what kinds of monads can't be filtered in the next installment

filterについてはもう少し話すことがあります。純粋にモナド的な観点でそれをどのように定義するか、どの種類のモナドがフィルターできないかといったことです。それは次回お話しします。

Conclusion for Part 2(パート2の結論)


"For" is a handy way to work with monads. Its syntax is particularly useful for working with Lists and other collections.

「for」はモナドとともに使う手近な手段です。そのシンタックスは特にリストや他のコレクションとともに使うと便利です。


But "for" is more general than that. It expands into map, flatMap, foreach, and filter. Of those, map and flatMap should be defined for any monad.

しかし「for」はそれよりもっと普遍的です。mapやflatMap、foreach、filterに展開されます。
その中でもmapとflatMapはあらゆるモナドで定義されるべきです。


The foreach method can be defined if you want the monad to be used imperatively and it's trivial to build. Filter can be defined for some monads but not for others.

foreachメソッドはモナドが命令的であるべきときに定義されます。実装は簡単です。
filterは定義してもよいモナドもありますがそうでないものもあります。


"m map f" can be implemented as "m flatMap {x => unit(x)}. "m foreach f" can be implemented in terms of map, or in terms of flatMap "m flatMap {x => unit(f(x));()}. Even "m filter p" can be implemented using flatMap (I'll show how next time). flatMap really is the heart of the beast.

「m map f」は「m flatMap {x => unit(x)}」として実装されます。「m foreach f」はmapの観点から実装するか、flatMapの観点から「m flatMap {x => unit(f(x));()}」として実装します。
「m filter p」さえflatMapを使って実装されます(次回方法を示します)。flatMapは本当に動物の心臓です。


Remember, monads are elephants. The picture I've painted of monads so far emphasizes collections. In part 4, I'll present a monad that isn't a collection and only a container in an abstract way. But before part 4 can happen, part 3 needs to cover some properties that are true of all monads: the monadic laws.

思い出してほしいのですが、モナドは象です。私が描いたモナドの像はこれまでコレクションを強調しました。
パート4ではコレクションでないモナドや抽象的な手段においてのみコンテナであるようなモナドを紹介します。
しかしパート4の前にパート3ですべてのモナドで真になるいくつかの属性、モナド則を解説する必要があります。


In the mean time, here's a cheat sheet showing how Haskell's do and Scala's for are related.

話は変わりますがHaskellのdoとScalaのforがどのように関連しているかを示すチートシートを載せます。


HaskellScala

do var1<- expn1
var2 <- expn2
expn3

for {var1 <- expn1;
var2 <- expn2;
result <- expn3
} yield result

do var1 <- expn1
var2 <- expn2
return expn3

for {var1 <- expn1;
var2 <- expn2;
} yield expn3

do var1 <- expn1 >> expn2
return expn3

for {_ <- expn1;
var1 <- expn2
} yield expn3