31

当使用 Existential 类型时,我们必须使用模式匹配语法来提取foralled 值。我们不能将普通的记录选择器用作函数。GHC 报告错误并建议使用以下定义的模式匹配yALL

{-# LANGUAGE ExistentialQuantification #-}

data ALL = forall a. Show a => ALL { theA :: a }
-- data ok

xALL :: ALL -> String
xALL (ALL a) = show a
-- pattern matching ok

-- ABOVE: heaven
-- BELOW: hell

yALL :: ALL -> String
yALL all = show $ theA all
-- record selector failed

forall.hs:11:19:
    Cannot use record selector `theA' as a function due to escaped type variables
    Probable fix: use pattern-matching syntax instead
    In the second argument of `($)', namely `theA all'
    In the expression: show $ theA all
    In an equation for `yALL': yALL all = show $ theA all

我的一些数据包含超过 5 个元素。如果我使用模式匹配,很难维护代码:

func1 (BigData _ _ _ _ elemx _ _) = func2 elemx

有没有一种好方法可以使这样的代码可维护或将其包装起来以便我可以使用某种选择器?

4

2 回答 2

22

存在类型以比常规类型更精细的方式工作。GHC(正确地)禁止您将theA其用作函数。但想象一下没有这样的禁令。该功能将具有什么类型?它必须是这样的:

-- Not a real type signature!
theA :: ALL -> t  -- for a fresh type t on each use of theA; t is an instance of Show

说的很粗暴,forall就是让 GHC “忘记”构造函数参数的类型;类型系统只知道这个类型是Show. 所以当你试图提取构造函数参数的值时,没有办法恢复原来的类型。

GHC 在幕后所做的是上面对假类型签名的注释所说的——每次你对ALL构造函数进行模式匹配时,绑定到构造函数值的变量被分配一个唯一的类型,该类型保证不同于其他所有类型. 以这段代码为例:

case ALL "foo" of
    ALL x -> show x

该变量x获得了一个独特的类型,该类型与程序中的所有其他类型都不同,并且不能与任何类型变量匹配。这些独特的类型不允许逃逸到顶层——这就是为什么theA不能用作函数的原因。

于 2012-04-17T17:42:33.530 回答
18

您可以在模式匹配中使用记录语法,

func1 BigData{ someField = elemx } = func2 elemx

有效,并且对于大型类型的打字要少得多。

于 2012-04-17T14:10:09.860 回答