Haskell自定义类型语法困惑:如何理解Card类型的定义逻辑
Haskell Card类型定义语法说明
你产生“用自身定义自身”的误解,本质是混淆了Haskell中data关键字定义代数数据类型时的两个独立概念:类型构造器(类型名)和值构造器(用来构造该类型值的函数)。在单构造器的自定义类型中,Haskell开发者习惯让二者同名,二者属于完全不同的命名空间,不存在递归定义的问题。
我们逐行拆解代码逻辑:
- 首先定义的是花色枚举类型
Suit
这里等号左侧的data Suit = Spades | Hearts | Clubs | Diamonds deriving (Show, Eq)Suit是类型构造器(即该类型的名称),右侧的Spades/Hearts等都是无参数的值构造器,每个值构造器直接对应一个Suit类型的合法值。 - 接下来定义的是牌面值类型
Rank
同样左侧data Rank = Numeric Int | Jack | Queen | King | Ace deriving ShowRank是类型名,右侧的Numeric是带参数的值构造器,本质是类型为Int -> Rank的函数,传入一个整数就能得到对应的数字牌面值,比如Numeric 7就代表7点的牌面;Jack/Queen等是无参数的值构造器,对应花牌和A。 - 最后是你疑惑的卡牌类型
Card定义data Card = Card Rank Suit deriving Show- 等号左侧的
Card是类型构造器,代表一种新的自定义类型,和前面的Suit、Rank、基础类型Int是同一层级的概念 - 等号右侧的
Card是值构造器,本质是类型为Rank -> Suit -> Card的函数,接收一个Rank类型的值和一个Suit类型的值作为参数,返回一个Card类型的卡牌实例,参数里没有用到Card类型本身,完全不是递归定义。
- 等号左侧的
举个实际构造卡牌的例子:要表示黑桃A,只需要调用值构造器Card,传入对应牌面值和花色即可:
spadesAce :: Card spadesAce = Card Ace Spades
如果要表示红桃5,写法为:
heartsFive :: Card heartsFive = Card (Numeric 5) Hearts
如果你担心同名混淆,也可以把值构造器命名为其他名称,比如:
data Card = MkCard Rank Suit deriving Show
这种写法和原代码功能完全一致,只是不符合通用命名习惯而已。
内容的提问来源于stack exchange,提问作者harleengulati03
相关产品推荐
相关产品推荐

