Fight the Future



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.


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


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.


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.


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


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.


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.


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.


"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.


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



do var1<- expn1
var2 <- expn2

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