のようなボックス化されていない型と、のようInt#
な厳密な関数f (!x) = ...
は何か違うものですが、概念的な類似性が見られます-それらは何らかの方法でサンク/怠惰を許可しません。HaskellがOcamlのような厳密な言語である場合、すべての関数は厳密であり、すべての型はボックス化されていません。ボックス化されていないタイプと強制的な厳密性の間にはどのような関係がありますか?
3 に答える
ボックス化されていないデータとボックス化されたデータ
パラメトリックポリモーフィズムと怠惰をサポートするために、デフォルトでは、Haskellデータ型は、次のような構造で、ヒープ上のクロージャーへのポインターとして均一に表されます。
(出典:haskell.org)
これらは「ボックス化された」値です。ボックス化されていないオブジェクトは、間接参照やクロージャなしで、値自体によって直接表されます。Int
箱入りですが、箱からInt#
出されています。
遅延値には、ボックス化された表現が必要です。厳密な値はそうではありません。ヒープ上で完全に評価されたクロージャとして、またはプリミティブなボックス化されていない構造として表すことができます。ポインターのタグ付けは、ボックス化されたオブジェクトで使用できる最適化であり、コンストラクターをクロージャーへのポインターにエンコードすることに注意してください。
厳格さとの関係
通常、ボックス化されていない値は、関数型言語コンパイラーによってアドホックな方法で生成されます。ただし、Haskellでは、ボックス化されていない値は特別です。彼ら:
- 彼らは別の種類を持っています、
#
; - 特別な場所でのみ使用できます。と
- それらは持ち上げられていないため、ヒープ値へのポインターとして表されません。
彼らは持ち上げられていないので、彼らは必然的に厳格です。怠惰の表現は不可能です。
したがって、、などの特定のボックス化されていない型はInt#
、Double#
実際にはマシン上でdoubleまたはintとして表されます(C表記)。
正格性解析
これとは別に、GHCは通常のHaskellタイプの厳密性分析を行います。値の使用が厳密であることが判明した場合(つまり、「未定義」になることはありません)、オプティマイザーは、の使用が常に厳密であることを認識しているため、通常のタイプ(たとえばInt
)のすべての使用をボックス化されていないタイプ()に置き換える可能性があります。したがって、より効率的な(そして常に厳密な)タイプとの交換は安全です。Int#
Int
Int#
もちろん、ボックス化されていない型なしで厳密な型を持つことができます。たとえば、要素-厳密なポリモーフィックリスト:
data List a = Empty | Cons !a (List a)
要素は厳密ですが、ボックス化されていない値としては表されません。
これは、 OCamlのような厳密な言語についてあなたが犯した間違いも指摘しています。それらは依然としてポリモーフィズムをサポートする必要があるため、統一された表現を提供するか、データ型と関数をすべての型に特化します。GHCは、OCamlと同様に、デフォルトで統一表現を使用しますが、GHCは(C ++テンプレートのように)タイプと関数を特殊化することもできます。
ボックス化されていない型は必ずしも厳密ですが、すべての厳密な値が必ずしもボックス化されていないわけではありません。
data Foo a = Foo !a !a
2つの厳密なフィールドがあります
data Bar a = Bar {-# UNPACK #-} !Int !a
2つの厳密なフィールドがありますが、最初のフィールドはボックス化されていません。
最終的に、ボックス化されていないタイプが(必然的に)厳密である理由は、その時点では単なるフラットでダムなデータであるため、サンクを格納する場所がないためです。
任意のタイプの引数を「厳密」にすることができますが、対応するボックス化されたタイプを持つボックス化されていないタイプはChar#
、、、、、Int#
およびWord#
のみDouble#
ですFloat#
。
Cのような低水準言語を知っているなら、説明するのは簡単です。ボックス化されていないタイプは、などのようなものint
でdouble
あり、ボックス化されたタイプは、などのようなものint*
ですdouble*
。を持っている場合int
、ビットパターンで表されるため、値全体がすでにわかっているため、怠惰ではありません。int
のすべての値は有効であり、⊥ではないため、これも厳密である必要があります。
ただし、与えられたint*
場合、後でポインタを逆参照して実際の値を取得することを選択でき(したがって遅延)、無効なポインタが存在する可能性があります(⊥、つまり非厳密)。