指定:
data Foo =
FooString String
…
class Fooable a where --(is this a good way to name this?)
toFoo :: a -> Foo
String
をFooable
のインスタンスにしたい:
instance Fooable String where
toFoo = FooString
次にGHCは不満を言う:
Illegal instance declaration for `Fooable String'
(All instance types must be of the form (T t1 ... tn)
where T is not a synonym.
Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Fooable String'
代わりに[Char]
:
instance Fooable [Char] where
toFoo = FooString
GHCは不平を言う:
Illegal instance declaration for `Fooable [Char]'
(All instance types must be of the form (T a1 ... an)
where a1 ... an are type *variables*,
and each type variable appears at most once in the instance head.
Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for `Fooable [Char]'
質問:
これは、String
が[Char]
の単なるタイプエイリアスであり、タイプコンストラクタ[]
をタイプChar
に適用しただけなので、これはフォーム([] Char)
。 Char
は型変数ではないため、(T a1 .. an)
の形式ではありません。
この制限の理由は、インスタンスの重複を防ぐためです。たとえば、instance Fooable [Char]
があり、後で誰かがやって来てinstance Fooable [a]
を定義したとします。これで、コンパイラーは使用したいものを判別できなくなり、エラーが発生します。
-XFlexibleInstances
を使用すると、基本的に、そのようなインスタンスを定義しないことをコンパイラーに約束できます。
達成しようとしていることに応じて、ラッパーを定義した方がよい場合があります。
newtype Wrapper = Wrapper String
instance Fooable Wrapper where
...
古典的なHaskell98型クラスの2つの制限に直面しています。
これらの厄介な制限は、2つの言語拡張によって解除されます。
-XTypeSynonymInstances
これにより、型の同義語([Char]
のString
など)を使用できるようになります。
-XFlexibleInstances
これは、T a b ..
という形式のインスタンスタイプの制限を解除します。パラメータはタイプ変数です。 -XFlexibleInstances
フラグを使用すると、インスタンス宣言のヘッドで任意のネストされた型を言及できます。
これらの制限を解除すると、 インスタンスの重複 が発生する可能性があることに注意してください。この場合、あいまいさを解決するために追加の言語拡張が必要になり、GHCがインスタンスを選択できるようになります。
参考文献::
FlexibleInstancesは、ほとんどの場合適切な答えではありません。より良い代替策は、Stringをnewtypeでラップするか、次のようにヘルパークラスを導入することです。
class Element a where
listToFoo :: [a] -> Foo
instance Element Char where
listToFoo = FooString
instance Element a => Fooable [a] where
toFoo = listToFoo
これらの回答に加えて、制限を解除することに不安がある場合は、クラスのインスタンスである可能性があるnewtypeで文字列をラップすることが理にかなっている場合があります。トレードオフは潜在的な醜さであり、コードでラップおよびアンラップする必要があります。