Fight the Future

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

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

One Div Zero: Monads are Elephants Part 2の翻訳続き。
なぜかはわからないけど、この翻訳で自分がレベルアップしているように感じる。
自分に足りない基礎概念の理解が深まっているような。。。そんな感覚。

Now With More Expression(さらに式を増やしてみる)


Let's kick it up a notch.

段階を上げてみましょう。

val qs = 
   for (n <- ns; o <- os; p <- ps)
      yield n * o * p

This "for" gets expanded into

この「for」は次のように展開されます。

val qs = ns flatMap {n => 
   os flatMap {o => 
      {ps map {p => n * o * p}}}}

That looks pretty similar to our previous "for." That's because the rule is recursive

これは先ほどの「for」とまったく同じように見えます。なぜならルールを繰り返しただけだからです。

for(x1 <- expr1;...x <- expr) 
   yield resultExpr

expands to
次のように展開されます。

expr1 flatMap {x1 => 
   for(...;x <- expr) yield resultExpr
}

This rule gets applied repeatedly until only one expression remains at which point the map form of expansion is used. Here's how the compiler expands the "val qs = for..." statement (again red italics turns to bold green)

このルールはmapを使う式が1つだけになるまで繰り返し適用されます。

  1. val qs = for (n <- ns; o <- os; p <- ps) yield n * o * p
  2. val qs = ns flatMap {n => for(o <- os; p <- ps) yield n * o * p}
  3. val qs = ns flatMap {n => os flatMap {o => for(p <- ps) yield n * o * p}}
  4. val qs = ns flatMap {n => os flatMap {o => {ps map {p => n * o * p}}}

An Imperative "For"(命令の「for」)

"For" also has an imperative version for the cases where you're only calling a function for its side effects. In it you just drop the yield statement.

「for」には副作用があるために関数を呼び出すだけにしたいという場合のために命令的なバージョンもあります。
そこではyieldステートメントを取るだけでいいです。

val ns = List(1, 2)
val os = List (4, 5)
for (n <- ns; o <- os)  println(n * o)

The expansion rule is much like the yield based version but foreach is used instead of flatMap or map.

展開のルールはyieldをベースにしたバージョンとすごく似ていますが、flatMapやmapの代わりにforeachを使います。

ns foreach {n => os foreach {o => println(n * o) }}

Now, you don't have to implement foreach if you don't want to use the imperative form of "for", but foreach is trivial to implement since we already have map.

さて、もし命令的な形の「for」を使うつもりがなければforeachを実装する必要はありません。
しかし、もうmapがあるのでforeachを実装するのは取るに足らないことです。

class M[A] {
   def map[B](f: A=> B) : M[B] = ...
   def flatMap[B](f: A => M[B]) : M[B] = ...
   def foreach[B](f: A=> B) : Unit = {
       map(f)
       ()
   }
}

In other words, foreach can just call map and throw away the results. That might not be the most runtime efficient way of doing things, though, so Scala allows you to define foreach your own way.

言い換えると、foreachは単にmapを呼び結果を利用しないだけです。
実行時にその処理を実現するもっとも効率的な方法ではないかもしれません。けれどもScalaはforeachを自分の方法で実装することを許可しています。