Haskellのデータ構造は一般的にlazyである。それによって、評価されたときにエラーやプログラム停止をもたらすような要素をデータ構造に含めることができる。lazyなデータ構造によってHaskellの表現を高めることができ、Haskellのプログラミングの重要な側面となっている。
内部的には、lazyなデータオブジェクトは、thunkと呼ばれる構造でラップされており、内部にエラーが含まれていても影響がない。たとえば、("a", ⊥)というタプルを保持することができる(⊥は未定義な値を表す)。一方、ほとんどのプログラミング言語は正格で、値が評価されてからデータ構造に入れられる。
ところが、thunkにはオーバーヘッドが大きいというデメリットがある。構築と評価に時間が必要だったり、heapにthunkのためのメモリが必要になる。
Haskellでは、このオーバーヘッドを避けるために、正格評価させるフィールドに正格フラグをつけることができる。正格フラグを付けた方が良いケースとしては、実行中の特定のタイミングで評価されるべきものや、評価がシンプルで決してエラーとならないもの、部分的な未定義値が意味をもたないものがある。
正格フラグの例としては、複素数ライブラリのComplex型があげられる。
data RealFloat a => Complex a = !a +: !a実部と虚部に正格フラグが付けられており、これらのフィールドには評価後の値が保持される。
正格フラグはデータコンストラクタでのみ使うことができる。逆に、関数の引数に対して正格処理をさせるようなフラグはないが、関数評価に!$演算子を使うことで同等のことを実現できる。
正格フラグを使うときの注意点としては、使用しないデータがメモリに残ってリークしがちな点がある。また、lazinessはHaskellの根本的な性質であり、正格フラグによってその性質を曲げることで、無限ループの発見が難しくなったり、予期せぬ結果をもたらす可能性はある。たとえば、循環定義がある場合には注意が必要である。
0 件のコメント:
コメントを投稿