1

Haskell 型クラスを理解しようとしています。以下が機能しないのはなぜですか?

{-# LANGUAGE FlexibleInstances #-}
class IntClass t
instance IntClass Int

intToIntClass  :: (IntClass r) => Int -> r
intToIntClass  x = x

明らかに、「インスタンス」は、私が意味するべきだと思うことを意味していません。代わりに、理解できないエラーメッセージが表示されます。

Could not deduce (r ~ Int)
from the context (IntClass r)
  bound by the type signature for intToIntClass  :: IntClass r => Int -> r
  at f.hs:10:1-16
  `r' is a rigid type variable bound by
      the type signature for intToIntClass  :: IntClass r => Int -> r
      at f.hs:10:1
In the expression: x
In an equation for `intToIntClass t': intToIntClass  x = x
4

4 に答える 4

14

署名

intToIntClass  :: (IntClass r) => Int -> r

intToIntClassは、その型が に属している限り、呼び出し元が必要とする型の値を生成できることを意味しIntClassます。しかし、実装は値しか生成できませんInt

Intコンパイラに関する限り、それが唯一のインスタンスであることは役に立ちません。他のモジュールで定義されたインスタンスがさらに存在する可能性があるため、コンパイラはInts のみを生成するメソッドを受け入れることができません。

于 2012-04-29T23:46:43.170 に答える
9

あなたが書いた関数は、「私に何かを与えてください。私はそれをあなたに返します」と言っています。あなたが書いた型は、「Intその型が型クラスのインスタンスである限り、呼び出し元が望む任意の型に変換できます」と述べていますIntClass

これらをまとめるとInt、呼び出し元から を受け取り、それを直接返す関数ができます。それとまったく同じものが返される場合を除いて、Intその型が型クラスのメンバーである限り、呼び出し元が望む任意の型の値になりますIntClass。入力をInt呼び出し元が望む任意の (制約付きの) 型にするにはどうすればよいでしょうか? できません。入力はそのまま返されるため、戻り値の型は引数の型と同じでなければなりません。型チェッカーはコードからこれを推測しますが、その推測を出力 type の命名と一致させることはできませんr。型チェッカーはこれについてあなたと協力したいと考えていますが、それが型クラスのインスタンスである唯一の仮説を与えられた場合rと同じであるとは確信できません。IntrIntClass

あなたが書いたものに似た関数はfromInteger、型クラスにありNumます。に基づいて構築できるすべての型について話したい場合はInt、これは型クラスのメソッドにする必要があります。何かのようなもの:

class IntClass a where
  intToIntClass :: Int -> a

のインスタンスにしたいすべての型に対してこのメ​​ソッドを定義しIntClass、目的の型を にしintToIntClass :: IntClass r => Int -> rます。この戻り型のポリモーフィズムは、関与する各型が の適切な定義を持つことに大きく依存しますintToIntClass

于 2012-04-29T23:50:10.283 に答える
4

あなたの型クラスは問題ありません。たとえば、これは機能します:

intId :: IntClass r => r -> r
intId = id

問題は、intToIntClassの型シグネチャが、 から の任意のメンバーにマップすると述べてIntいることですIntClassIntコンパイラは が の唯一のメンバーであることを証明できないためIntClass、 の本体はintToIntClass厳密に を返すためInt、十分に多態的ではありません。

于 2012-04-29T23:48:26.163 に答える
3

さらに明確にするために、モジュールを新しいインスタンスと一緒に使用するとします。

instance IntClass Integer

次に、 of の実装intToIntClassも type を持つことを約束します(IntClass Integer => Int -> Integer)。しかし、実装はこれと競合します。コンパイラは、型クラスが「オープン」であると想定します。つまり、

  1. 将来、新しいインスタンスが出現する可能性があります。
  2. 今日のコードは、そのような将来の動作を変更してはなりません

したがって、コンパイラは、現在は唯一のインスタンスが for でIntあることを認識していますが、これが将来も真であり続けるとは想定できません。

(1.) と (2.) の組み合わせにより、Haskell コードについての思考と推論がはるかに簡単になります。今日の新しいインスタンスが昨日のコードを台無しにすることはなく、今日のコードが明日の新しいインスタンスから安全であることがわかっている場合、警告ははるかに少なくなります。

于 2012-04-30T07:06:58.647 に答える