2

最小限のサンプルコード:

class IntegralAsType a where
  value :: (Integral b) => a -> b

class (Num a, Fractional a, IntegralAsType q) => Zq q a | a -> q where
  changeBase:: forall p b . (Zq q a, Zq p b) => a -> b

newtype (IntegralAsType q, Integral a) => ZqBasic q a = ZqBasic a deriving (Eq)

zqBasic :: forall q a . (IntegralAsType q, Integral a) => a -> ZqBasic q a
zqBasic x = ZqBasic (mod x (value (undefined::q)))

instance (IntegralAsType q, Integral a) => Zq q (ZqBasic q a) where
  changeBase (ZqBasic x) = fromIntegral (value (undefined::p)) -- ZqBasic is an instance of Num

これが私が達成しようとしていることの背景です。IntegralAsTypeは、モジュラスが異なる2つの数値の加算などを防ぐことにより、コンパイル時の型の安全性を保証します。ZqBasicはZqタイプの内部表現であり、他にもあります。そのため、Zqはそのように定義されています。目標は、内部表現に対して透過的なシステムを取得することです。

私の問題はchangeBase関数にあります。'p'型で明示的なforallを使用していますが、それでも「値の使用から生じる制約(IntegralAsType a0)のあいまいな型変数a0」を取得します。

なぜこのエラーが発生するのか混乱しています。特に前回の投稿では、changeBase関数と同じ設定になっているように見える「zqBasic」関数のようなものについて助けを得ました。明示的な数量詞'forallqa'を追加することにより、zqBasicのあいまいな変数エラーを修正しました。この数量詞がないと、あいまいな型変数エラーが発生します。なぜそこに数量詞が必要なのかは理解できますが、それがchangeBaseに役立たないように見える理由はわかりません。

ありがとう

4

3 に答える 3

3

使用しているものはとにかく範囲内にないScopedTypeVariablesため、使用はここでは役に立ちません。p次の定義を比較してください。

changeBase (ZqBasic x) = fromIntegral (value (undefined::foobar))

これにより、新しい型変数も作成されるため、同じエラーが発生します。

changeBase (ZqBasic x) = fromIntegral (value (5::p))

ただし、これにより別のエラーが発生します。関連するビットは次のとおりです。

Could not deduce (Num p1) arising from the literal `5'
        (snip)
    or from (Zq q (ZqBasic q a), Zq p b)
      bound by the type signature for
                 changeBase :: (Zq q (ZqBasic q a), Zq p b) => ZqBasic q a -> b

これは、pが新しい型変数としてインスタンス化されていることを示しています。forall型シグニチャのonは、クラスの型パラメータではない型変数を(実際の宣言では)スコープに入れないと推測しています。ただし、変数はデフォルト宣言のスコープに含まれます。

とにかく、それはここにもそこにもありません。

型変数へのアクセスが必要な場合のほとんどの問題を回避するのは簡単です。何もしないが、型を適切に操作できるようにする補助関数を作成するだけです。たとえば、このようなナンセンス関数は、ファントムタイプの用語を想起させるふりをします。

zq :: (Zq q a) => a -> q
zq _ = undefined

これは基本的に、関数従属性への直接の用語レベルのアクセスを提供するだけです。これは、fundepがq特定のに対して明確になるためaです。もちろん、実際の値を取得することはできませんが、それは重要ではありません。undefined気になる場合は、[] :: [q]代わりに同様の効果を使用しheadて、必要な場合にのみ使用してください。

これで、句などを使用して少しひねりwhere、正しい型を強制的に推測できます。

instance (IntegralAsType q, Integral a) => Zq q (ZqBasic q a) where
  changeBase (ZqBasic x) = b
    where b = fromIntegral (value (zq b))

ここで起こっているのbは、実際に必要なことですが、のタイプによって決定されるvalueタイプを確認する必要があるため、結果に一時的な名前を割り当てることで、それを使用して必要なタイプを取得できます。pb

多くの場合、追加の定義なしでこれを行うこともできますが、型クラスでは、アドホック多相性を回避し、「再帰」が他のインスタンスを巻き込まないようにする必要があります。

関連する注意点として、標準ライブラリ関数asTypeOfは、まさにこのタイプのタイプをいじるためのものです。

于 2011-12-02T15:11:27.470 に答える
1

呼び出しはからあるタイプにvalue (undefined::p)変換されます。のタイプから、私たちが理解できる唯一のことは、それが積分タイプであるということです。pa0valuea0

その値はに渡されfromIntegral、はに変換さa0bます。のタイプから、fromIntegral私たちが理解できる唯一のことは、それa0が積分タイプであるということです。

タイプが何であるかを決定するものは何もないため、あいまいさを解決するためa0に式にタイプ注釈が必要です。value (undefined::p)型シグネチャを見ると、value余分な変換を行わなくても正しい戻り型を生成できるはずです。の呼び出しを簡単に削除できますfromIntegralか?

編集

ScopedTypeVariables拡張機能を有効にする必要があります。がないScopedTypeVariablesと、型変数を複数の型シグニチャで指定することはできません。この場合、変数名pは、関数の型シグニチャーとその本体で同じ変数を参照していません。

次のコードは私のために働きます:

instance (IntegralAsType q, Integral a) => Zq q (ZqBasic q a) where
  changeBase = zqBasicChangeBase

zqBasicChangeBase :: forall p b q a.
  (IntegralAsType q, IntegralAsType p, Integral a, Zq p b) => ZqBasic q a -> b
zqBasicChangeBase (ZqBasic x) = fromIntegral (value (undefined :: p) :: Integer)
于 2011-12-02T06:36:07.663 に答える
0

動作する2つの(本質的に同等ですか?)アプローチ:

1:CA McCannのアプローチを使用する:

instance (IntegralAsType q, Integral a) => Zq q (ZqBasic q a) where
  changeBase (ZqBasic x) = b
    where b = fromIntegral ((value (zq b))*(fromIntegral (value (zq q)))) -- note that 'q' IS in scope

2:署名a-> bを使用するのではなく、changeBaseの署名をb->aに変更しました。次のように機能します。

instance (IntegralAsType q, Integral a) => Zq q (ZqBasic q a) where
  changeBase x = fromIntegral ((value (zq x))*(fromIntegral (value (zq q)))) -- note that 'q' IS in scope

目標は常に引数と戻り型の両方の型パラメーターにアクセスできるようにすることでしたが、これらのアプローチの両方でそれが可能になりました。

また、「zqBasic」コンストラクターと「changeBase」の違いに関する私の質問に対する答えは、CAMcAnnが示したように、明示的なforallを使用しても、「p」は宣言のスコープに入れられなかったということです。これがなぜなのか誰かが説明できれば、私はそれをいただければ幸いです。

于 2011-12-02T16:37:46.050 に答える