(1) は失敗するが (2) は失敗しない場合は些細なことです。型シノニム ( type ExampleOfATypeSynonym = ...
) はインスタンス宣言では許可されていませんが、制約では許可されているため、次のようなインスタンスが1 つしかない状況はすべて次のようになります。
-- (1)
class Foo a
type Bla = ()
instance Foo Bla
... 次のように変換できます。
-- (2)
class Foo a
type Bla = ()
instance (a ~ Bla) => Foo a
(1) が失敗する唯一の理由は、型シノニムがインスタンス宣言で許可されていないためです。これは、型シノニムが型関数に似ているためです。型名から型名への一方向のマッピングを提供します。type B = A
および の場合、代わりに のインスタンスが作成さinstance Foo B
れることは自明ではありません。Foo A
ルールが存在するため、実際にインスタンスを取得するタイプであることをinstance Foo A
明確にするために代わりに記述する必要があります。
このコンテキストでは、型ファミリの使用は関係ありません。問題は、型シノニムである型を使用していることにあるからですNameRecord
。また、型シノニムが削除されてFieldOf Name
直接置き換えられた場合でも、コンパイルは失敗することに注意する必要があります。これは、「型ファミリ」が型シノニムの単なる拡張バージョンであるため、このコンテキストでFieldOf Name
は「型シノニム」でもあるためです。Name :> Text
「双方向」の関連付けを取得するには、代わりにデータ ファミリとデータ インスタンスを使用する必要があります。
データ ファミリの詳細については、GHC のドキュメントを参照してください。
「... (2) は失敗するが (1) は失敗する...」という意味だと思います
次のような型クラスがあるとします。
class Foo a where
foo :: a
これで、次のようにインスタンスを記述できます。
instance Foo Int where
foo = 0
instance Foo Float where
foo = 0
main :: IO ()
main = print (foo :: Float)
これは期待どおりに機能します。ただし、コードを次のように変換すると:
{-# LANGUAGE FlexibleInstances, TypeFamilies #-}
class Foo a where
foo :: a
instance (a ~ Int) => Foo a where
foo = 0
instance (a ~ Float) => Foo a where
foo = 0
main :: IO ()
main = print (foo :: Float)
コンパイルされません。次のエラーが表示されます。
test.hs:5:10:
Duplicate instance declarations:
instance a ~ Int => Foo a -- Defined at test.hs:5:10-27
instance a ~ Float => Foo a -- Defined at test.hs:8:10-29
したがって、これはあなたが探していた例です。Foo
現在、これは、このトリックを使用するのインスタンスが複数ある場合にのみ発生します。何故ですか?
GHC が型クラスを解決するとき、インスタンス宣言の先頭だけを調べます。つまり、 の前のすべてを無視します=>
。インスタンスを選択すると、それに「コミット」し、その前に制約をチェックして、=>
それらが真であるかどうかを確認します。したがって、最初に 2 つのインスタンスが表示されます。
instance Foo a where ...
instance Foo a where ...
この情報だけに基づいて、どのインスタンスを使用するかを決定することは明らかに不可能です。