Constraint
という種通常、制約(型の中で=>
の左に出現するもの)の構文は極めて強く制限されている。次のものにしかなれない。
-XConstraintKinds
フラグを使うと、これより広い範囲ものを制約として受け付けるようになる。正確にいうと、このフラグのもとでは新しい種Constraint
を持つあらゆる型を制約として使える。以下のものが種Constraint
を持つ。
Constraint
を持つようなタプル。よって、たとえば型(Show a, Ord a)
の種はConstraint
になる。形はまだ分かっていないが、ユーザが種Constraint
(これはGHC.Exts
からインポートする)を持つとして宣言したもの全て。よって例えばtype Foo (f :: * -> Constraint) = forall b. f b => b -> b
は許される。また次の型族を使った例も許される。
type family Typ a b :: Constraint type instance Typ Int b = Show b type instance Typ Bool b = Num b func :: Typ a b => a -> b -> b func = ...
制約は特定の種を持った単なる型として扱われるので、この拡張によって制約シノニムが可能になる。
type Stringy a = (Read a, Show a) foo :: Stringy a => a -> (String, String -> a) foo x = (show x, read)
現在、標準的な制約およびタプルと、これら二種類の制約を基にしたシノニムのみが、インスタンス文脈とスーパークラス文脈で(別のフラグなしで)使用できる。この理由は、もっと一般的な制約を許すと型検査のループを引き起し得るからである。以下の二つのプログラムはそのような例である。
type family Clsish u a type instance Clsish () a = Cls a class Clsish () a => Cls a where
class OkCls a where type family OkClsish u a type instance OkClsish () a = OkCls a instance OkClsish () a => OkCls a where
インスタンス文脈やスーパークラスに変わった種類の制約を使うプログラムを書くことはできるが、それには-XUndecidableInstances
を使って、型検査器が終了しなくても気にしないことを示す必要がある。