存在型包装必需品

时间:2012-06-03 13:56:10

标签: haskell polymorphism existential-type higher-rank-types

事实证明,尽管背后有非常简单的想法,但仍然难以正确使用存在/等级n类型。

为什么需要将存在类型包装到data类型中?

我有以下简单示例:

{-# LANGUAGE RankNTypes, ImpredicativeTypes, ExistentialQuantification #-}
module Main where

c :: Double
c = 3

-- Moving `forall` clause from here to the front of the type tuple does not help,
-- error is the same
lists :: [(Int, forall a. Show a => Int -> a)]
lists = [ (1, \x -> x)
        , (2, \x -> show x)
        , (3, \x -> c^x)
        ]

data HRF = forall a. Show a => HRF (Int -> a)

lists' :: [(Int, HRF)]
lists' = [ (1, HRF $ \x -> x)
         , (2, HRF $ \x -> show x)
         , (3, HRF $ \x -> c^x)
         ]

如果我注释掉lists的定义,则代码会成功编译。如果我不发表评论,我会收到以下错误:

test.hs:8:21:
    Could not deduce (a ~ Int)
    from the context (Show a)
      bound by a type expected by the context: Show a => Int -> a
      at test.hs:8:11-22
      `a' is a rigid type variable bound by
          a type expected by the context: Show a => Int -> a at test.hs:8:11
    In the expression: x
    In the expression: \ x -> x
    In the expression: (1, \ x -> x)

test.hs:9:21:
    Could not deduce (a ~ [Char])
    from the context (Show a)
      bound by a type expected by the context: Show a => Int -> a
      at test.hs:9:11-27
      `a' is a rigid type variable bound by
          a type expected by the context: Show a => Int -> a at test.hs:9:11
    In the return type of a call of `show'
    In the expression: show x
    In the expression: \ x -> show x

test.hs:10:21:
    Could not deduce (a ~ Double)
    from the context (Show a)
      bound by a type expected by the context: Show a => Int -> a
      at test.hs:10:11-24
      `a' is a rigid type variable bound by
          a type expected by the context: Show a => Int -> a at test.hs:10:11
    In the first argument of `(^)', namely `c'
    In the expression: c ^ x
    In the expression: \ x -> c ^ x
Failed, modules loaded: none.

为什么会这样?第二个例子不应该等同于第一个例子吗? n级类型的这些用法有什么区别?当我想要这种多态时,是否有可能省略额外的ADT定义并仅使用简单类型?

3 个答案:

答案 0 :(得分:6)

您的第一次尝试使用存在类型。而不是你的

lists :: [(Int, forall a. Show a => Int -> a)]

要求第二个组件可以提供我选择的任何可显示类型的元素,而不仅仅是您选择的某些可显示类型。你正在寻找

lists :: [(Int, exists a. Show a * (Int -> a))]  -- not real Haskell

但这不是你所说的。数据类型打包方法允许您通过currying从exists恢复forall

HRF :: forall a. Show a => (Int -> a) -> HRF

表示要构建HRF值,您必须提供包含a类型的三元组,Show的{​​{1}}字典和{{1}中的函数}}。也就是说,a构造函数的类型有效地压缩了这个非类型

Int -> a

您可以通过使用rank-n类型对教会编码存在主义来避免数据类型方法

HRF

但这可能有点过头了。

答案 1 :(得分:3)

这些例子并不等同。第一,

lists :: [(Int, forall a. Show a => Int -> a)]

表示每个第二个组件都可以生成任何所需的类型,只要它是来自Show的{​​{1}}的实例。

第二个例子是模块等效于

的类型包装器
Int

即,每个第二个组件可以从lists :: [(Int, exists a. Show a => Int -> a)] 生成属于Show某些类型,但您不知道哪个类型的函数回报。

此外,您放入元组的函数不满足第一个合同:

Int

lists = [ (1, \x -> x) , (2, \x -> show x) , (3, \x -> c^x) ] 生成的结果与输入结果相同,所以这里是\x -> xInt始终生成show,因此它是一种固定类型。最后,String生成与\x -> c^x相同的类型,即c

答案 2 :(得分:3)

关于存在类型的处理,您必须考虑两个问题:

  • 如果您存储“可以show n的任何内容”,则必须存储可以与如何显示一起显示的内容。
  • 实际变量只能有一种类型。

第一点是为什么你必须有存在类型包装器,因为当你写这个:

data Showable = Show a => Showable a

实际发生的是这样的事情被声明:

data Showable a = Showable (instance of Show a) a

即。对Show a的类实例的引用与值a一起存储。如果没有这种情况发生,你就无法拥有一个展开Showable的函数,因为该函数不知道如何展示a

第二点是为什么你有时会得到某种类型的怪癖,以及为什么你不能在let绑定中绑定存在类型,例如。


您的代码中的推理也存在问题。如果你有一个像forall a . Show a => (Int -> a)这样的函数,你会得到一个函数,给定任何整数,它将能够产生调用者选择的任何类型的可显示值。你可能意思是exists a . Show a => (Int -> a),这意味着如果函数得到一个整数,它将返回一个存在Show实例的类型。