13

なぜこれが競合を引き起こすのですか?

class Foo a b | b -> a where
  foo :: a -> b -> Bool

instance Eq a => Foo a a where
  foo = (==)

instance Eq a => Foo a (a -> a) where
  foo x f = f x == x

機能依存を削除すると、コードがコンパイルされることに注意してください。

私は、機能依存性は、実際にはコンパイルされるときに、次のようなものだけを許可するべきではないという印象を受けました!

class Foo a b | b -> a where
  foo :: a -> b -> Bool

instance Eq a => Foo a a where
  foo = (==)

instance Eq a => Foo Bool a where
  foo _ x = x == x

同じbパラメーターですが、パラメーターが異なりaます。これは、 ?によって一意に決定されるb -> aことを意味するため、これを禁止するべきではありません。ab

4

2 に答える 2

15

セカンドバージョンを実際に使ってみましたか?インスタンスのコンパイル中に、 を呼び出すとあいまいさとオーバーラップ エラーが発生し始めると思いますfoo

ここでの最大の障害は、Fundeps が期待どおりに型変数と対話しないことです。インスタンスの選択は実際には解決策を探しません。単一化を試みてやみくもに一致させるだけです。具体的には、 と書くFoo a aと、aは完全に恣意的であり、したがって のような型と統合できb -> bます。したがって、2 番目のパラメーターの形式b -> bが の場合、両方のインスタンスに一致しますが、ファンデプスは、ある場合は最初のパラメーターが である必要がb -> bあるが、他の場合は である必要があると述べていますb。したがって、競合。


これは明らかに人々を驚かせるので、2 番目のバージョンを使用しようとすると、次のようになります。

  • bar = foo () ()結果:

    Couldn't match type `Bool' with `()'
      When using functional dependencies to combine
        Foo Bool a,
    

    ... Fundep は、2 番目のインスタンスを介して、2 番目のパラメーターとしての任意の型Boolが最初のパラメーターとして一意に決定されると言うからです。したがって、最初のパラメーターは でなければなりませんBool

  • bar = foo True ()結果:

    Couldn't match type `()' with `Bool'
      When using functional dependencies to combine
        Foo a a,
    

    ... fundep は、最初のインスタンスを介して、2 番目のパラメーターとしての任意の型が最初のパラメーターと同じ型を一意に決定することを示しているためです。したがって、最初のパラメーターは でなければなりません()

  • bar = foo () True今回は、最初のパラメーターが であるべきであることに同意しているため、両方のインスタンスが原因でエラーが発生しますBool

  • bar = foo True True結果:

    Overlapping instances for Foo Bool Bool
      arising from a use of `foo'
    

    ...両方のインスタンスが満たされているため、重複しています。

かなり楽しいですね。

于 2011-09-15T19:25:41.023 に答える
2

最初のインスタンスは for any aと言い、次に fundep は を返しますa。これは、他のほとんどすべてを除外することを意味します。これは、他のものはその自由変数と統合し、そのインスタンスの選択を強制する必要があるためです。

編集:最初は、2番目の例が機能することを示唆していました。これは ghc 7.0.4 で行われましたが、そうする意味がありませんでした。この問題は、新しいバージョンでは解決されているようです。

于 2011-09-15T19:31:20.443 に答える