10

aタイプのいくつかの値関数の最適な値を計算することを仕事とする関数がありますa -> v

type OptiF a v = (a -> v) -> a

次に、値の値を使用する別の関数と一緒にそのような関数を格納するコンテナーがあります。

data Container a = forall v. (Ord v) => Cons (OptiF a v) (a -> Int)

のインスタンスであることを除いて、 type の関数を実装する人は誰でもOptiF a vの詳細に悩まされるべきではないという考えです。vOrd

そのため、そのような値関数とコンテナーを取る関数を作成しました。を使用するOptiF a vと、最適な値 wrt が計算valされ、コンテナーのresult関数にプラグインされます。

optimize :: (forall v. (Ord v) => a -> v) -> Container a -> Int
optimize val (Cons opti result) = result (opti val)

ここまでは順調ですが、 を呼び出すことはできませんoptimize

callOptimize :: Int
callOptimize = optimize val cont
   where val = (*3)
         opti val' = if val' 1 > val' 0 then 100 else -100
         cont = Cons opti (*2)

コンパイルしません:

Could not deduce (v ~ Int)
from the context (Ord v)
  bound by a type expected by the context: Ord v => Int -> v
  at bla.hs:12:16-32
  `v' is a rigid type variable bound by
      a type expected by the context: Ord v => Int -> v at bla.hs:12:16
Expected type: Int
  Actual type: Int
Expected type: Int -> v
  Actual type: Int -> Int
In the first argument of `optimize', namely `val'
In the expression: optimize val cont

行 12:16-32 はoptimize val contです。

この場合、存在型を誤解していますか? それが望むものは何でも期待できるという意味forall vの宣言の中にありますか? それとも、それ以外には何も期待できないということですか?optimizeoptimizea -> vvoptimizea -> vOrd v

私が欲しいのは、後でプラグインしたいので、OptiF a vがどの にも固定されていないことです。私が課したい唯一の制約は. 存在型(または何でも)を使用してそのようなものを表現することさえ可能ですか?va -> vOrd v

optimizeと同様のシグネチャを持つ関数を提供する追加の型クラスでそれを達成することができましたが、OptiF a vそれは高階関数を使用するよりもずっと醜く見えます。

4

1 に答える 1

12

これは間違いやすいものです。

あなたが持っている署名は実存的なものでoptimizeなく、普遍的なものです。

...とにかく存在論はやや時代遅れなので、データを GADT 形式に書き直してみましょう。これにより、構文は基本的に多相関数の場合と同じであるため、ポイントがより明確になります。

data Container a where
  (:/->) :: Ord v =>                       -- come on, you can't call this `Cons`!
     OptiF a v -> (a->Int) -> Container a

制約Ord(ここでは であることを意味するforall v...) は、型変数のパラメーター化された関数シグネチャの外側にあることに注意してください。つまり、 valuevを構築したいときに外部から指示できるパラメーターContainerです。言い換えると、

全体vとして、コンストラクターOrd が存在します(:/->) :: OptiF a v -> (a->Int) -> Container a

それが「実在型」という名前の由来です。繰り返しますが、これは通常の多相関数に似ています。

一方、サインでは

optimize :: (forall v. (Ord v) => a -> v) -> Container a -> Int

署名用語自体の内部にa がありますforall。つまり、具体的な型vが取り得るものは、呼び出し先によってoptimize内部的に決定されます。外部から制御できるのは、それが にあることだけですOrd。それについて「存在する」ものは何もありません。そのため、この署名は実際には単独でコンパイルされXExistentialQuantificationません。XGADTs

<interactive>:37:26:
    Illegal symbol '.' in type
    Perhaps you intended -XRankNTypes or similar flag
    to enable explicit-forall syntax: forall <tvs>. <type>

val = (*3)明らかに満たしていません。(forall v. (Ord v) => a -> v)実際には、すべての s が持っているNumわけではないインスタンスが必要です。Ord実際、optimizerank2 型は必要ありません。呼び出し元が指定する可能性のある任意のOrd型で機能するはずです。v

optimize :: Ord v => (a -> v) -> Container a -> Int

ただし、その場合、実装は機能しなくなります。実際には存在するコンストラクターであるため不明なタイプ関数(:/->)のみを含める必要があります。したがって、optimize の呼び出し元は、そのような特定の型の opti-function を選択する自由と、他の固定された型に対して最適化される関数を自由に選択できます。これは機能しません! OptiFv1

あなたが望む解決策はこれです:Containerどちらも存在するべきではありません!opti 関数はOrd、特定の 1 つのタイプだけでなく、 にあるすべてのタイプで機能するはずです。さて、GADT として、これは最初に持っていた普遍的に量化された署名とほぼ同じに見えますoptimize

data Container a where
  (:/->) :: (forall v. Ord v => OptiF a v) -> (a->Int) -> Container a

これで、最適化が機能します

optimize :: Ord v => (a -> v) -> Container a -> Int
optimize val (opti :/-> result) = result (opti val)

そして、あなたが望むように使用することができます

callOptimize :: Int
callOptimize = optimize val cont
   where val = (*3)
         opti val' = if val' 1 > val' 0 then 100 else -100
         cont = opti :/-> (*2)
于 2013-02-28T02:22:26.233 に答える