事实证明,尽管它们背后的想法非常简单,但正确使用存在/排名-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定义,只使用简单的类型?
发布于 2012-06-03 22:06:41
关于存在类型的处理,您必须考虑两个问题:
如果存储"anything that can be shown“,则必须存储可显示的内容,以及如何显示实际变量只能有一种类型。
第一点是为什么你必须有存在类型包装器,因为当你写这个的时候:
data Showable = Show a => Showable a实际发生的情况是像这样的东西被声明:
data Showable a = Showable (instance of Show a) a即,与值a一起存储对Show a的类实例的引用。如果不这样做,您就不能拥有一个解开Showable的函数,因为该函数不知道如何显示a。
第二点是为什么你有时会遇到一些类型怪异,以及为什么你不能在let绑定中绑定存在类型。
你在代码中的推理也有问题。如果你有一个像forall a . Show a => (Int -> a)这样的函数,你会得到一个给定任意整数的函数,它将能够产生调用者选择的任何类型的可显示值。您可能指的是exists a . Show a => (Int -> a),这意味着如果函数获得一个整数,它将返回某个存在Show实例的类型。
发布于 2012-06-03 22:12:13
您的第一次尝试不是使用存在类型。而是你的
lists :: [(Int, forall a. Show a => Int -> a)]要求第二个组件可以提供我选择的任何可显示类型的元素,而不仅仅是您选择的某种可显示类型。你要找的是
lists :: [(Int, exists a. Show a * (Int -> a))] -- not real Haskell但你不是这么说的。数据类型打包方法允许您通过刷新从forall恢复exists。你有
HRF :: forall a. Show a => (Int -> a) -> HRF这意味着要构建HRF值,必须提供一个包含类型a的三元组、一个用于a的Show字典和一个Int -> a中的函数。也就是说,HRF构造函数的类型有效地限制了这个非类型
HRF :: (exists a. Show a * (Int -> a)) -> HRF -- not real Haskell您可以通过使用rank类型来对存在词进行教会编码,从而避免使用datatype方法
type HRF = forall x. (forall a. Show a => (Int -> a) -> x) -> x但这可能有点过头了。
发布于 2012-06-03 22:06:32
这些例子是不等价的。第一,
lists :: [(Int, forall a. Show a => Int -> a)]表示每个第二个组件都可以生成任何所需的类型,只要它是来自Int的Show实例。
第二个示例是对类型包装进行取模,该类型包装等同于
lists :: [(Int, exists a. Show a => Int -> a)]也就是说,每个第二个组件都可以从Int中产生属于Show的某种类型,但您不知道该函数返回哪种类型。
此外,放入元组中的函数不满足第一个约定:
lists = [ (1, \x -> x)
, (2, \x -> show x)
, (3, \x -> c^x)
]\x -> x生成的类型与作为输入给出的结果的类型相同,所以这里的类型是Int。show总是生成一个String,所以它是一个固定的类型。最后,\x -> c^x生成与c相同的类型,即Double。
https://stackoverflow.com/questions/10870945
复制相似问题