11

型クラスをいじって、一見無害に見えるものを思いついた

class Pair p a | p -> a where
  one :: p -> a
  two :: p -> a

これはうまくいくようです、例えば

instance Pair [a] a where
  one [x,_] = x
  two [_,y] = y 

しかし、私はタプルに困っています。次の定義はコンパイルされますが...

instance Pair (a,a) a where
  one p = fst p 
  two p = snd p

...期待どおりに使用できません:

main = print $ two (3, 4)

No instance for (Pair (t, t1) a)
  arising from a use of `two' at src\Main.hs:593:15-23
Possible fix: add an instance declaration for (Pair (t, t1) a)
In the second argument of `($)', namely `two (3, 4)'
In the expression: print $ two (3, 4)
In the definition of `main': main = print $ two (3, 4)

インスタンスを正しく定義する方法はありますか? newtypeそれとも、ラッパーに頼る必要がありますか?

4

2 に答える 2

18

実際、インスタンスは問題なく機能します。観察:

main = print $ two (3 :: Int, 4 :: Int)

これは期待どおりに機能します。では、なぜ型アノテーションなしでは機能しないのでしょうか?さて、タプルのタイプを考えてみましょう:(3, 4) :: (Num t, Num t1) => (t, t1)。数値リテラルはポリモーフィックであるため、同じタイプである必要はありません。インスタンスはに対して定義されて(a, a)いますが、そのインスタンスが存在しても、GHCにタイプを統合するようには指示されません(さまざまな理由により)。GHCが他の方法で2つのタイプが同じであると推測できない限り、2つのタイプを等しくすることができたとしても、GHCは必要なインスタンスを選択しません。

問題を解決するには、上記のように型注釈を追加するだけです。引数が他の場所からのものである場合、それらは同じ型であることがすでにわかっているため、通常は不要ですが、数値リテラルを使用する場合はすぐに不器用になります。

別の解決策は、インスタンスの選択がどのように機能するかという理由で、のインスタンスがあるということは、必要な場合でも同様(a, a)にインスタンスを作成できないことを意味することに注意することです。(a, b)したがって、次のように、型クラスを使用して統合を強制するために、少しごまかすことができます。

instance (a ~ b) => Pair (a,b) a where

それTypeFamiliesにはコンテキストの拡張が必要~だと思います。インスタンスの選択ではコンテキストが無視されるため、これにより、インスタンスは最初は任意のタプルで一致するようになります。ただし、インスタンスを選択した後、a ~ bコンテキストは型の同等性をアサートします。これは、型が異なる場合はエラーを生成しますが、可能であれば、ここでさらに重要なことですが、型変数を統合します。mainこれを使用すると、注釈なしで定義がそのまま機能します。

于 2011-12-16T15:52:42.973 に答える
6

問題は、リテラル数に多相型があることです。両方のリテラルが同じ型 ( Int) を持つ必要があることは、型チェッカーには明らかではありません。タプルにポリモーフィックではないものを使用すると、コードが機能するはずです。次の例を検討してください。

*Main> two (3,4)

<interactive>:1:1:
    No instance for (Pair (t0, t1) a0)
      arising from a use of `two'
    Possible fix: add an instance declaration for (Pair (t0, t1) a0)
    In the expression: two (3, 4)
    In an equation for `it': it = two (3, 4)
*Main> let f = id :: Int -> Int -- Force a monomorphic type
*Main> two (f 3,f 4)
4
*Main> two ('a','b')
'b'
*Main> two ("foo","bar")
"bar"
*Main> two (('a':),('b':)) "cde"
"bcde"
*Main>
于 2011-12-16T15:52:59.867 に答える