Haskell で副作用を使うには、IO モナドや ST モナドを使います。IO モナド は main アクションで入出力を実現するのに使われているおなじみのモナド ですが、ST モナドは main アクション以外にも副作用を導入出来ます。
また、副作用のある配列として Data.Vector.Mutable があります。 Data.Array.ST にも副作用版配列はありますが、Vector の方が高速なので、 ここでは Vector を用いる方法を紹介します。ひょっとすると標準では Data.Vector.Mutable は使えないかもしれませんので、cabal コマンドで vector と vector-algorithms をインストールする必要があるかもしれません。
とりあえず簡単に ST モナドの使い方を見てみましょう。筆者も ST モナドの ことを完全に理解しているわけではありませんので、間違い等があっても 自己責任でお願いします。リストを逆順にする reverse 関数の副作用版を以下 に示します。
sideEffect
が副作用のある関数で、modify
関数
を通して呼び出します。このとき modify
は副作用のない配列を
入力として受け取り、副作用のある可変配列 as
に変換して
sideEffect
に渡します。sideEffect
ではこの
可変配列 as
を加工します。そして modify
は
可変配列 as
を受け取り副作用の無い配列に変換して戻り値に
します。fromList
はリストを配列に変換し、toList
は配列をリストに変換します。
副作用のある関数の特徴は戻り値の型です。sideEffect
の
戻り値の型は ST s ()
になっています。これで
sideEffect
が ST モナドの中に入ります。()
は、
sideEffect
が擬似的なプロシージャであることを表しています。
もし、プロシージャではなく戻り値が必要なときは、ST s Int
の
ように返す値の型を ()
の代わりに書きます。ただし、ここでは
modify
が ST s ()
を要求するので、
ST s ()
にしてあります。なお、可変配列の型は
STVector s a
です。
when
という関数を知らない人もいるかもしれません。これは、
手続き型言語の if 文のように振る舞う関数です。第1引数が True なら、
第2引数を実行します。第1引数が False なら、return ()
が
実行されます。大雑把に言って何も実行されないのと同じです。
MV.length
は as
の長さを求める関数です。
unsafeRead, unsafeWrite
はそれぞれ副作用のある可変配列の
読み書きです。説明せずとも見れば分かりますよね。さあ、これであなたも
Haskell で副作用のある関数が書けるようになりました!(キリッ)
じゃんじゃん書いてください。この他にも副作用を導入する方法に
runST
を使う方法があり、こちらの方が汎用的ですが、これは
自分で調べてみてくださいね。