読者です 読者をやめる 読者になる 読者になる

yunomuのブログ

酒とゲームと上から目線

Haskellで並列処理

あの、関数型言語の特徴としてよく「並列化しやすい」とか挙げられますよね。なんでかっていうのはなんかよくわかんないんだけど、やっぱりその、副作用っていうか、他のスレッドに影響を及ぼすような処理を書きづらいというか、影響を及ぼさない処理を書きやすいというか、そういう感じなんでしょうけど、よくわかんないけど。
でも何故か純粋関数型を謳っているHaskellでそういう話はあんまし聞いたことなくて、いやせっかく副作用無いんだからどんどん並列化していこうよ、とか思ったりもする。
ということでとりあえずやってみました。

そのへんについて簡単なのないかなと適当にググるとControl.Parallelのparとかpseqを使ったサンプルとか出てきて、まあ簡単なのかもしれないけど結局使い道がよくわかんねぇよってなって、どうしようってなる。っていうかなりました。
もっと簡単な例は無いのか。

ということで、私が遭遇したのは多分一番簡単な例です。多分ググれば例はいくらでも出てくるタイプだと思います。

やったこと:
「URLのリスト」を「URLで指定されたHTMLのリスト」に変換する。

サンプルコードも動作確認も何も無いけどだいたいこういう感じになるんじゃないでしょうか。

uris :: [URI]
curl :: URI -> IO String

main :: IO ()
main = do
  articles <- mapM curl uris
  print articles

curlという、URIからHTMLに変換するっていうか、URIを渡されるとHTTPを喋ってHTMLを取ってくる関数を使う。

ここでmapMの挙動を考えると、urisに入ってるURIを前から順番に評価して、1個目の処理が終わるまで次のcurlは動かないという感じになると思われる。
でもcurlって別に次の処理との関係は無いから並列に実行しちゃって良い。

そこでmonad-parallelパッケージに入ってるControl.Monad.Parallelを使います。

import Control.Monad.Parallel

とやるとmapMが並列になります。
おわりです。まあ簡単。
確かにmapMはそのまま並列化できるよなぁ。これはちょっと目からウロコというか。

全部やっちゃうと微妙なことも多々あるし元のmapMとも被るので普通はこういう感じにするんじゃないでしょうか。

import qualified Control.Monad.Parallel as P

(略)
 P.mapM curl uris

mapM_とかは無いみたいです。些細な事ですが。

この辺やるとなんとなく関数型言語の良さがわかってきますね。