高阶函数中的存在类型

时间:2013-02-28 00:54:32

标签: haskell existential-type

我有一个函数,其职责是计算a类型的某个最佳值,以及a -> v类型的某个值函数

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

然后我有一个容器想要将这样的函数与另一个使用值值的函数一起存储:

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

这个想法是,实现OptiF a v类型函数的人不应该被v的细节所困扰,除非它是Ord的实例。

所以我编写了一个带有这样的值函数和容器的函数。使用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声明中的optimize是否意味着optimize可能期望a -> v它想要的v是什么?或者这是否意味着optimize除了a -> v之外Ord v可能没有任何意义?

我想要的是OptiF a v并未针对任何v修复,因为我想稍后插入一些a -> v。我想要强加的唯一约束是Ord v。是否有可能使用存在类型(或其他)来表达类似的内容?

我设法通过一个额外的类型类来实现这一点,该类型为optimize函数提供了与OptiF a v类似的签名,但这对我来说比使用更高阶函数更加丑陋。

1 个答案:

答案 0 :(得分:12)

这很容易出错。

optimize签名中的内容是存在主义,而是普遍存在。

...因为existentials有点过时了,让我们将你的数据重写为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...)位于类型变量参数化函数签名之外,即 v是我们可以参数当我们想要构建Container时,从外部指示。换句话说,

  

对于v 中的所有Ord,存在构造函数(:/->) :: OptiF a v -> (a->Int) -> Container a

这就是“存在型”的名称。同样,这类似于普通的多态函数。

另一方面,在签名

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

您在签名字词本身中有一个forall,这意味着{em>被调用者,v可以确定具体类型optimize。 ,在内部 - 我们从外部控制的是它在Ord。没有什么“存在主义”,这就是为什么这个签名实际上不会仅用XExistentialQuantificationXGADTs进行编译的原因:

<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),它实际上需要Num个实例,并非所有Ord都有。实际上,optimize不应该使用rank2类型:它应该适用于调用者可能提供给它的任何Ord - 类型v

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

在这种情况下,您的实现不再起作用了:因为(:/->)实际上是一个存在主义的构造函数,它只需要包含任何 OptiF函数,因为< em> some 未知类型v1。因此,优化调用者可以自由选择任何特定类型的opti函数,并且可以针对任何可能的其他固定类型进行优化 - 这是无法工作的!

您想要的解决方案是: Container不应该是存在的,! opti-function应该适用于Ord中的任何类型,而不仅仅适用于某种特定类型。好吧,作为一个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)