軽率多相(Levity Polymorphism)
種の使用方法に完全な柔軟性を持たせるためには,種システムを使用して,BoxedかつLiftedな型(
Int
や
[Bool]
などの通常で平凡な型)と,
Int#
のようなUnboxedかつプリミティブな型(
Unboxed型とプリミティブ操作)を区別する必要がある.ゆえに我々は
軽率多相(levity polymorphism)と呼ばれるものを持っている.
以下は GHC.Exts
から入手可能な主要な定義である.
lp01.hsTYPE :: RuntimeRep -> Type -- highly magical, built into GHC
data RuntimeRep = LiftedRep -- for things like `Int`
| UnliftedRep -- for things like `Array#`
| IntRep -- for `Int#`
| TupleRep [RuntimeRep] -- unboxed tuples, indexed by the representations of the elements
| SumRep [RuntimeRep] -- unboxed sums, indexed by the representations of the disjuncts
| ...
type Type = TYPE LiftedRep -- Type is just an ordinary type synonym
要するに, RuntimeRep
によってパラメータ化される新しい基本型定数 TYPE
があるということだ.したがって, Int# :: TYPE 'IntRep
および Bool :: TYPE 'LiftedRep
が得られる. TYPE x
という形式の型を持つものはすべて,関数矢印 ->
の両側に現れることができる.つまり, ->
は TYPE r1 -> TYPE r2 -> TYPE 'LiftedRep
という型を持つということがいえる.GHCではすべての関数はLiftedになるため,その結果は常にLiftedになる.
軽率多相変数や引数はない
GHCが現実世界で動作するプログラムをコンパイルする必要がなければ,それで話は終わりだ.しかしはRepresentation Polymorphism(訳注:わからん)はGHCのコードジェネレータにかなりの問題を引き起こす可能性がある.以下の例を考てみよう.
lp02.hsbad :: forall (r1 :: RuntimeRep) (r2 :: RuntimeRep)
(a :: TYPE r1) (b :: TYPE r2).
(a -> b) -> a -> b
bad f x = f x
これは一見して標準の $
演算子の一般化のように見える.しかし,これを実行可能なコードにコンパイルすることを考えた場合,問題が発生する.特に, bad
を呼ぶときには,どうにかして x
を bad
に渡さなければならない. x
の幅(つまりビット数はどれくらいだろうか?それはポインタだろうか?どのような種類のレジスタ(浮動小数点または整数)に x
を入れるべきか? x
の型, a :: TYPE r1
は軽率多相なので,これらの疑問に答えることは不可能である.そのため,次のような簡単な規則により,このような構成を禁止する.
どの変数も軽率多相な型を持つことはできない.
これは,変数xがRepresentation Polymorhic型を持つことになるので, bad
は違法である.
しかし,すべてが失われるわけではない.我々はまだ以下のようなコードを書くことができる.
lp03.hs($) :: forall r (a :: Type) (b :: TYPE r).
(a -> b) -> a -> b
f $ x = f x
ここでは, b
のみが軽率多相である.軽率多相な型を持つ変数はない.そしてコードジェネレータはこれで問題ない.実際に,これはGHCの $
演算子の本当の型で,Haskell 98バージョンよりも少し一般的である.
コードジェネレータは変数だけでなく引数もストアおよび移動する必要があるため,上記のロジックは関数の引数にも同様に適用され,関数の引数も軽率多相になることができない.
軽率多相ボトム
我々は軽率多相を error
と undefined
に関して効果的に用いることができる.これらの型は以下の通りである.
lp04.hsundefined :: forall (r :: RuntimeRep) (a :: TYPE r).
HasCallStack => a
error :: forall (r :: RuntimeRep) (a :: TYPE r).
HasCallStack => String -> a
これらの関数は軽率多相な変数を束縛しないので受け入れられる.その多相性により,ユーザはこれらを使用してUnboxed型を返すスタブの関数を作ることができ便利である.
軽率多相型のプリンティング
-fprint-explicit-runtime-reps
RuntimeRep
パラメータを表示どおりにプリントする.そうでなければ,それらはデフォルトで 'LiftedRep
に設定される.
ほとんどのGHCユーザは軽率多相やUnboxed型について心配する必要はない.これらのユーザにとっては, $
の型の軽率多相を見ることは役に立たない.したがって,デフォルトでは,プリントするときには RuntimeRep
型のすべての型変数は 'LiftedRep
になると仮定し, TYPE 'LiftedRep
を Type
(または StarIsType
が有効であれば, *
)としてプリントすることによって,これは抑制されている.
あなたの型の軽率多相を見たいのなら,フラグ -fprint-explicit-runtime-reps
を有効にせよ.