4

これは初心者の質問ですが、どこにも答えがわかりません。
次のコード:

class A a where
  foo :: a
class A a => B a where
  bar :: a
  bar = (foo :: a)

GHCでのコンパイルに失敗し、次のエラーメッセージが表示されます。

Could not deduce (A a1) arising from a use of `foo'
from the context (B a)
  bound by the class declaration for `B'
...

GHCは、型クラスBの定義のすべてのaが同じであると確信していないようです。誰かがその推論の線が正確に何であるかを説明できますか?

5行目のタイプアノテーションを削除すると、もちろん問題は回避されますが、ここで何が起こっているのかを理解したいと思います...

4

2 に答える 2

11

確かに、型の注釈を取り除く必要があります。型変数はHaskellでスコープされていないので、(foo :: a)。「任意の型fooの型の値を生成する」と解釈されます。これは、クラスにある型の値のみを生成するため、実行できません。aafooaA

言い換えれば、あなたの宣言はBと同等です

class A a => B a where
  bar :: a
  bar = (foo :: c)

つまり、型変数aの使用と宣言での他の使用との間に関係はありません。

明示的なアノテーションを削除すると、問題が解決します。

class A a => B a where
  bar :: a
  bar = foo

これで、コンパイラは、呼び出したいタイプfoo、つまり、aシグニチャにbar記述し、クラス宣言の先頭に表示されるタイプを判別できます。

Glasgow Haskellコンパイラ(GHC)には、スコープ付き型変数を可能にする拡張機能が付属しています。その拡張機能を有効にすると、フラグメントは当初の予想どおりにタイプチェックされます。

{-# LANGUAGE ScopedTypeVariables #-}
class A a where
  foo :: a
class A a => B a where
  bar :: a
  bar = (foo :: a)
于 2012-04-20T10:02:38.533 に答える
2
Prelude> :set -XScopedTypeVariables
Prelude> :{
Prelude| class A a where
Prelude|   foo :: a
Prelude| class A a => B a where
Prelude|   bar :: a
Prelude|   bar = (foo :: a)
Prelude| :}
Prelude> :t bar
bar :: B a => a
Prelude> 

しかし、dblheloxが言ったように、ここでスコープ付き型変数を使用する必要はありません。

于 2012-04-20T10:07:23.467 に答える