这篇文章遗憾地缺乏关于Haskell中ADT发展的参考文献,来自A History of Haskell: Being Lazy With Class,第5.1节:
通常,代数类型指定一个或多个的总和 替代方案,其中每个替代品是零或更多的产品 领域。允许总和为零可能是有用的 替代品,这将是一个完全空的类型,但在当时 这种类型的价值不受欢迎。
让我想知道,这样的ADT将如何有用?
答案 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
这里我们有两个没有构造函数的数据类型。它们的含义仅用于表示自然数:Zero
,Succ Zero
,Succ (Succ Zero)
等。它们在Vec
数据类型中用作phantom types,以便我们可以编码其类型中矢量的长度。然后,我们可以编写类型安全的函数,例如vhead
/ vtail
,它们只能应用于非空向量。
另见[Haskell] Fixed-length vectors in Haskell, Part 1: Using GADTs详细阐述的例子。
答案 2 :(得分:2)
没有办法构造无构造函数类型的“真实”值(其中“真实”是指终止计算; Haskell有undefined :: a
,error :: 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。
中的页面