C#とScalaのモナド
2018-01-19 07:15:18
『モナドの驚異』にアクセスが多いようなので、便乗してモナドの記事を書いてみる。
Haskellのdo記法に当たる構文糖衣
C#ではLINQのクエリ構文が用意されている。Scalaの場合はfor式だ。
var list1 = Enumerable.Range(0, 5);
var list2 = from x in list1
select x * 10;
var list3 = from x in list2
from y in list1
select x + y;
val list1 = 0 to 5
val list2 = for (x <- list1) yield x * 10
val list3 = for (x <- list2; y <- list1) yield x + y
C#のクエリ構文は、Select
メソッドまたはSelectMany
メソッドの呼び出しに機械的に置き換えられる。Scalaのfor式は、map
メソッドやflatMap
メソッドの呼び出しに機械的に置き換えられる。それらのメソッドが見つからないときは、クエリ構文やfor式はコンパイルエラーになる。
var list1 = Enumerable.Range(0, 5);
var list2 = list1.Select(x => x * 10);
var list3 = list2.SelectMany(x => list1, (x, y) => x + y);
val list1 = 0 to 5
val list2 = list1.map(x => x * 10)
val list3 = list2.flatMap(x => list1.map(y => x + y))
C#のSelectMany
はmap
とbind
(>>=
) が合成されたような形をしているが、ScalaのflatMap
はbind
(>>=
) そのものだ。
public Option<TResult> SelectMany<T2, TResult>(
Func<T, Option<T2>> otherSelector,
Func<T, T2, TResult> resultSelector);
final def flatMap[B](f: (A) ⇒ Option[B]): Option[B]
Haskellのdo
記法にはreturn
は必須ではない。return
はモナドを返す単なる関数であり、別に使わなくてもdo
の最後にモナドがあれば問題ない。
たとえば、print
は Show a => a -> IO ()
のようにIOモナドを返すので、こう書ける。
main = do
args <- getArgs
print args
C#のクエリ構文はselectが必須だし、Scalaのモナディックなfor式で値を返す時にはyieldが必須なので、Haskellのようには書けない。