技術をかじる猫

適当に気になった技術や言語、思ったこと考えた事など。

反論あったのでメモ

http://d.hatena.ne.jp/tanakmura/20121119

純粋関数型言語について言えば実に正しい気がする。そういえば関数型といいつつScalaとかをベースに物を考えていた気もする。不覚。

前回 http://d.hatena.ne.jp/white-azalea/20121027/1351337261 の主張は、「個々の処理が独立(参照透過性確保)してるなら、並列で走らせていいよね(不具合でないよね)。関数型はそうなる(参照透過性を持つ関数が書きやすい)よう言語仕様がサポートしてるけど、言語に限った話じゃ無いからそういう設計を心がけようね」って話だった。

そこ来ると論点がずれた気がしないではない*1

  • ☓:関数型言語は並列プログラミングに向いてる
  • ○:参照透過性は並列プログラミングに向いてる(それを言語仕様でサポートする非純粋関数型言語の一部はそれによく向いている)

というだけの話で、純粋関数型言語自体が並列に向くかは私もNoだと思ってる。

少なくとも、Haskell がスレッドと相性が悪いのはその通り


1.
そもそもHaskell(他の言語実装は知らんが)の最適化アプローチは「参照透過性確保」という依存性排除による結果のメモ化を遅延評価と一緒に行なっている(実行が遅延している間、既に評価されたものと同一引数であればその結果を使いまわせる)。
これは、「結果をキャッシュするから余計な処理しなくていいよね」ってだけなので、スレッド化したらキャッシュを共有せねばならず、それをすると同期とかでロスもあれば、関数的にも「参照透過性がある」とはいえず、マルチスレッドとは相容れない。

2.
指摘の通り遅延評価はマルチスレッドと相性が悪い。遅延評価は評価(実処理)を遅らせて、本当に必要になるまで処理をしないという最適化だ。これにより使われなかった値は評価(処理)されず、結果むだな処理が省かれる、、、と。つまりコード上その値を使用しない限りは関数スタックが積まれて実行待ちになるだけなのだ。
しかし、スレッド化するという概念は、実行を並列化するという概念である以上、そのスタックを強制的にでも評価せざるを得ない。
Haskellなどの)純粋関数型言語は遅延評価をスタックを積み切ってから実行環境まで持っていく為にモナドまで編み出しているのだが、そこくると相性が悪い。

この辺が「Haskellの自動並列化は難しい」は上記の絡みじゃないかと思わなくない。

そもそもスレッド自体、「手続きを並列で走らせる」というものなのだから、手続きと直行した純粋関数型言語と相性が悪いのはそらそうだ。

(2013/1/14 追記:これはコンパイラ実装の話なので言語仕様の問題かは不明だが、「たらい回し関数」をHaskesll/GHCでコンパイルしたらバイナリに直接結果を書き込まれたことがあった。コンパイルの時点で展開できるところは自動で展開されるのかも?そこ来ると、勝手に展開される最適化はスレッドと相性が更に悪い筈)

*1:元記事が「関数型言語は並列プログラムに向いてるは幻想」だったので、その流れで勘違いされた可能性があるほか、上記の記事も純粋関数型と非純粋関数型を明示的に区別しておらず、誤解しやすいのは確か