Haskellがとっつきにくい原因の一つに遅延評価がある。入門書では、無限リストと遅延評価がことさら強調される。しかし、Haskellを業務で使ってみると、遅延評価が煩わしくなってくる。遅延評価なしでもほとんどのことは実現できるし、メモリーの使用量は推測できないし、あまりいいことはない。
Haskellの評価戦略が、他の言語と同じように正格評価だったらよかったのに。
今まで、このようなセリフを何度聞いたか分からない。 そもそも遅延評価が役立つことはあるのだろうか?
ある。お世辞抜きに、少なくとも以下の3つでは本当に役立つ。
- リスト(あるいは類似のデータ構造)処理
- 純粋性に対する暗黙のテスト
- 効率的なCAS
1.はよいだろう。2.は純粋さを守るために必要だが、コンパイラを開発する人にとって重要なのであり、ユーザには関係ない。3.は、並行プログラミングの奥義である。atomicModifyIORef'
を用いれば、実際の仕事を待機させた状態で compare-and-swap を成功させ、後からその仕事を片付けられる。
しかしながら私は、これ以外で遅延評価が役に立った事例を寡聞にして知らない。
実はHaskellでも、正格評価を利用できる。それには !
(バンと読む)を使う。
{-# LANGUAGE BangPatterns #-} data T = C !Int f !x = y where !y = x + 1
この問題点は、コードのいたるところがバンバンして、読みにくくなることである。実際、コードを書いてみると嫌になってくる。この問題を解決するために、GHC 8.0 では、言語拡張 Strict
と StrictData
が提供された。この二つを使えば、デフォルトの評価戦略が正格評価となる。
つまり、以下のコードの評価戦略は遅延評価だが、Strict
と StrictData
を用いると正格評価となる。
data T = C Int f x = y where y = x + 1
ちなみに、この言語拡張が有効な範囲は指定されたモジュール内のみである。
デフォルトの評価戦略を正格評価にすると、今度は遅延評価を明示的に書けるようにする必要がある。それには ~
を使う。
data T = C ~Int f ~x = y where ~y = x + 1
Strict
と StrictData
をもう少しく知りたい人はStrict Haskellを読んでほしい。
純粋関数型データ構造を読んだ諸君、Haskellではデフォルトが遅延評価だからイマイチ例題をうまく実装できないと思ったことだろう。でも、今なら簡単にできるのだ!
GHC 8.0.1がリリースされたのは、2016年5月。あれから2年強。現在は、GHC 8.6の時代だ。GHCのメジャーバージョンは3つサポートする掟に鑑みてもお釣りがくる。
Haskellerよ、時は来た。新しいプロジェクトを始めるときは、迷わずに以下を cabalファイルに入れるのだ!
default-extensions: Strict StrictData