具有零构造函数的代数数据类型有什么意义?

时间:2012-08-15 11:51:42

标签: haskell types algebraic-data-types

这篇文章遗憾地缺乏关于Haskell中ADT发展的参考文献,来自A History of Haskell: Being Lazy With Class,第5.1节:

  

通常,代数类型指定一个或多个的总和   替代方案,其中每个替代品是零或更多的产品   领域。允许总和为零可能是有用的   替代品,这将是一个完全空的类型,但在当时   这种类型的价值不受欢迎。

让我想知道,这样的ADT将如何有用?

4 个答案:

答案 0 :(得分:9)

理论上:库里 - 霍华德的同构性使我们将这种类型解释为“虚假”命题。 “虚假”本身就是一个有用的命题;但对于构造“非”组合子(如type Not a = a -> False)和其他类似结构也很有用。

务实:此类型可用于防止参数化数据类型的某些分支出现。例如,我在库中使用它来解析各种类似的游戏树:

data RuleSet a            = Known !a | Unknown String
data GoRuleChoices        = Japanese | Chinese
data LinesOfActionChoices -- there are none in the spec!
type GoRuleSet            = RuleSet GoRuleChoices
type LinesOfActionRuleSet = RuleSet LinesOfActionChoices

这样做的影响是,在解析一行动作游戏树时,如果指定了一个规则集,我们知道它的构造函数将是Unknown,并且可以让其他分支关闭在模式匹配等期间

答案 1 :(得分:3)

对应于逻辑 false (如另一个答案中所述),它们通常用于与GADTs组合创建其他类型约束。例如:

{-# LANGUAGE GADTs #-}
{-# LANGUAGE EmptyDataDecls #-}

import Data.List (groupBy)

data Zero
data Succ n

data Vec n a where
    Nil  ::                 Vec Zero a
    Cons :: a -> Vec n a -> Vec (Succ n) a

vhead :: Vec (Succ n) a -> a
vhead (Cons v _) = v

vtail :: Vec (Succ n) a -> Vec n a
vtail (Cons _ v) = v

这里我们有两个没有构造函数的数据类型。它们的含义仅用于表示自然数:ZeroSucc ZeroSucc (Succ Zero)等。它们在Vec数据类型中用作phantom types,以便我们可以编码其类型中矢量的长度。然后,我们可以编写类型安全的函数,例如vhead / vtail,它们只能应用于非空向量。

另见[Haskell] Fixed-length vectors in Haskell, Part 1: Using GADTs详细阐述的例子。

答案 2 :(得分:2)

没有办法构造无构造函数类型的“真实”值(其中“真实”是指终止计算; Haskell有undefined :: aerror :: String -> a以及写作的可能性像mwahaha = mwahaha这样的非终止程序,简单易懂,我称之为“假”值。

这可能有用的一个示例是conduit库的0.5及更高版本。库中的基本类型是Pipe l i o u m r;对于这些类型的不同参数,Pipe可以用作(生成输出而不消耗任何输入),接收器(消耗输入而不生成任何输出),或管道(消耗输入并产生输出)。 i的{​​{1}}和o类型参数分别是其输入和输出的类型。

因此,管道库强制执行以下概念:源不消耗输入和接收器的概念通过使用Data.Void中的Pipe类型作为源的输入类型和接收器的输出类型来产生无输出。同样,没有终止方法来构造这种类型的值,因此尝试从接收器消耗输出的程序将不会终止(作为提醒,在Haskell中这可能意味着“引发错误”而不一定“永远循环“)。

答案 3 :(得分:0)

没有构造函数的类型称为幻像类型。请参阅Haskell wiki

中的页面