web-dev-qa-db-ja.com

Stringを型クラスのインスタンスにできないのはなぜですか?

指定

data Foo =
  FooString String
  …

class Fooable a where --(is this a good way to name this?)
  toFoo :: a -> Foo

StringFooableのインスタンスにしたい:

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]'

質問

  • タイプクラスの文字列とインスタンスを作成できないのはなぜですか?
  • 余分なフラグを追加すると、GHCはこれを回避する用意があります。これは良い考えですか?
84
John F. Miller

これは、String[Char]の単なるタイプエイリアスであり、タイプコンストラクタ[]をタイプCharに適用しただけなので、これはフォーム([] Char)Charは型変数ではないため、(T a1 .. an)の形式ではありません。

この制限の理由は、インスタンスの重複を防ぐためです。たとえば、instance Fooable [Char]があり、後で誰かがやって来てinstance Fooable [a]を定義したとします。これで、コンパイラーは使用したいものを判別できなくなり、エラーが発生します。

-XFlexibleInstancesを使用すると、基本的に、そのようなインスタンスを定義しないことをコンパイラーに約束できます。

達成しようとしていることに応じて、ラッパーを定義した方がよい場合があります。

newtype Wrapper = Wrapper String
instance Fooable Wrapper where
    ...
65
hammar

古典的なHaskell98型クラスの2つの制限に直面しています。

  • それらはインスタンスの型の同義語を許可しません
  • それらは順番に型変数を含まないネストされた型を許可しません。

これらの厄介な制限は、2つの言語拡張によって解除されます。

  • -XTypeSynonymInstances

これにより、型の同義語([Char]Stringなど)を使用できるようになります。

  • -XFlexibleInstances

これは、T a b ..という形式のインスタンスタイプの制限を解除します。パラメータはタイプ変数です。 -XFlexibleInstancesフラグを使用すると、インスタンス宣言のヘッドで任意のネストされた型を言及できます。

これらの制限を解除すると、 インスタンスの重複 が発生する可能性があることに注意してください。この場合、あいまいさを解決するために追加の言語拡張が必要になり、GHCがインスタンスを選択できるようになります。


参考文献:

18
Don Stewart

FlexibleInstancesは、ほとんどの場合適切な答えではありません。より良い代替策は、Stringをnewtypeでラップするか、次のようにヘルパークラスを導入することです。

class Element a where
   listToFoo :: [a] -> Foo

instance Element Char where
   listToFoo = FooString

instance Element a => Fooable [a] where
   toFoo = listToFoo

参照: http://www.haskell.org/haskellwiki/List_instance

4
Lemming

これらの回答に加えて、制限を解除することに不安がある場合は、クラスのインスタンスである可能性があるnewtypeで文字列をラップすることが理にかなっている場合があります。トレードオフは潜在的な醜さであり、コードでラップおよびアンラップする必要があります。

2
kowey