すべてのタイプa
とタイプb
の関数は、タイプforall a. forall b. a -> b
の値を取り、タイプa
の値を生成できる必要がありますb
。したがって、たとえば、を入れInt
たり取り出したりできる必要がありString
ます。
あなたが入れた場合、あなたはString
抜け出すことができません-あなたはあなたが入れたのと同じタイプを取り戻すだけです。したがって、タイプではありません。実際、型クラスの制約がなければ、その型の完全な関数はあり得ません。id
Int
id
forall a. forall b. a -> b
結局のところ、ConstraintKindsを使用して、やりたいことに近いことをすることができますが、書くのも使うのもきれいではありません。
アイデアは、、、g
によって満たされる必要のある条件、およびとの間の関係、およびとの間の関係を指定する制約を使用x
してパラメーターy
化することです。すべての場合にこれらすべての制約が必要なわけではないため、2つのダミー型クラス(1つは個々のパラメーターの制約用、もう1つは「関係制約」用)も導入し、制約が不要な制約として使用できるようにします(制約を自分で指定しないと、GHCは制約を推測できません)。u
v
x
u
y
v
これをより明確にするためのいくつかの制約例:
id
関数として渡す場合は、x
と等しくなければならず、と等しくu
なけれy
ばなりませんv
。x
、、または個別y
に制約はありません。u
v
- を渡す場合
show
、x
およびy
はとのインスタンスである必要がありShow
、u
とv
はに等しくなければなりませんString
。x
andu
またはy
andとの関係に制約はありませんv
。
- を渡すと
read . show
、x
とy
のインスタンスである必要がShow
ありu
、のインスタンスでv
ある必要がありますRead
。ここでも、それらの間の関係に制約はありません。
- 型クラスが
Convert a b where convert :: a -> b
あり、を渡す場合、個々のパラメーターに制約convert
は必要Convert x u
ありません。Convert y v
したがって、これを実装するコードは次のとおりです。
{-# LANGUAGE Rank2Types, ConstraintKinds, FlexibleInstances, MultiParamTypeClasses #-}
class Dummy a
instance Dummy a
class Dummy2 a b
instance Dummy2 a b
g :: forall c. forall d. forall e. forall x. forall y. forall u. forall v.
(c x, c y, d u, d v, e x u, e y v) =>
(forall a. forall b. (c a, d b, e a b) => a -> b) -> x -> y -> (u,v)
g p x y = (p x, p y)
そして、これがそれを使用する方法です:
show . read
異なるタイプの数値を変換するために使用する:
> (g :: (Show x, Show y, Read u, Read v, Dummy2 x u, Dummy2 y v) => (forall a. forall b. (Show a, Read b, Dummy2 a b) => a -> b) -> x -> y -> (u,v)) (read . show) 1 2 :: (Double, Int)
(1.0,2)
使用id
:
> (g :: (Dummy x, Dummy y, x~u, y~v) => (forall a. forall b. (Dummy a, Dummy b, a~b) => a -> b) -> x -> y -> (u,v)) id 1 2.0
(1,2.0)
使用show
:
> (g :: (Show x, Show y, String~u, String~v, Dummy2 x u, Dummy2 x y) => (forall a. forall b. (Show a, String~b, Dummy2 a b) => a -> b) -> x -> y -> (u,v)) show 1 2.0
("1","2.0")
ご覧のとおり、g
使用するたびに署名を指定する必要があるため、これはひどく冗長で読みにくいものです。これがないと、GHCに制約を正しく推測させることはできないと思います(または少なくとも方法がわかりません)。