You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell中如何在ADT内定义非Int类型的披萨尺寸常量?

解决Haskell披萨尺寸ADT关联自定义数值的方案

下面是几种符合需求的实现方式,可根据你的场景选择:

方案1:保留原枚举ADT,添加映射函数

这种方式最直接,保留你原来的枚举定义,通过单独的函数把每个尺寸映射到自定义的Inch类型,既保留模式匹配的便利性,又能关联语义明确的数值:

-- 自定义长度类型,明确语义为英寸
newtype Inch = Inch Double deriving (Show, Eq)

-- 原有的尺寸枚举ADT
data Size = Small | Medium | Large | ExtraLarge deriving (Show, Eq)

-- 映射函数:将尺寸转换为对应英寸值
sizeToInch :: Size -> Inch
sizeToInch Small = Inch 9.5
sizeToInch Medium = Inch 12.0
sizeToInch Large = Inch 14.0
sizeToInch ExtraLarge = Inch 16.0

优点

  • 完全保留原ADT的结构,模式匹配逻辑无需修改
  • 自定义Inch类型避免了通用数值(如Int)的语义模糊问题
  • 实现简单,适合玩具程序快速迭代

缺点

  • 添加新尺寸时,需要同时修改ADT定义和映射函数,容易遗漏(可通过编写测试用例规避)

方案2:智能构造器+封装尺寸值

如果想严格限制只能创建合法的披萨尺寸,避免用户构造无效值,可以用智能构造器封装内部实现,只暴露预定义的合法尺寸:

newtype Inch = Inch Double deriving (Show, Eq)

-- 隐藏内部构造器,仅通过预定义变量创建实例
data Size = Size Inch deriving (Show)

-- 暴露的合法尺寸
small, medium, large, extraLarge :: Size
small = Size (Inch 9.5)
medium = Size (Inch 12.0)
large = Size (Inch 14.0)
extraLarge = Size (Inch 16.0)

-- 可选:添加判断尺寸类型的辅助函数
isSmall :: Size -> Bool
isSmall (Size (Inch 9.5)) = True
isSmall _ = False

优点

  • 彻底杜绝无效尺寸的创建,保证数据合法性
  • 尺寸和数值绑定紧密,无需额外映射函数

缺点

  • 原有的枚举模式匹配需要调整(比如要匹配Size inch而非直接的Small)
  • 判断具体尺寸需要额外编写辅助函数

方案3:类型类实现尺寸关联

如果以后需要给其他类型(比如饼底厚度、配料分量)也添加尺寸属性,可以用类型类实现复用:

newtype Inch = Inch Double deriving (Show, Eq)

data Size = Small | Medium | Large | ExtraLarge deriving (Show, Eq, Enum, Bounded)

-- 定义通用的尺寸类型类
class HasSize a where
  getSize :: a -> Inch

-- 让Size实例化类型类
instance HasSize Size where
  getSize Small = Inch 9.5
  getSize Medium = Inch 12.0
  getSize Large = Inch 14.0
  getSize ExtraLarge = Inch 16.0

优点

  • 扩展性强,其他需要尺寸属性的类型可直接实例化HasSize
  • 保留原ADT的枚举结构,模式匹配不受影响

缺点

  • 比方案1稍复杂,玩具程序可能没必要用到这么多抽象

内容的提问来源于stack exchange,提问作者Jir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 17:18:10