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

Haskell定义数据类型时如何指定约束?以交易簿模型为例

解决交易簿模型的非法状态问题

要让Book的buy列表仅包含Buy订单、sell列表仅包含Sell订单,核心是通过类型系统或构造函数约束,从根源上杜绝非法状态的创建。以下是两种实用方案:

方案一:使用幽灵类型(Phantom Type)编译期约束

幽灵类型能在编译阶段就阻止错误的订单方向混入,完全符合“让非法状态无法表示”的原则。

代码实现

data Side = Buy | Sell deriving (Show, Eq)

-- 用幽灵类型标记订单的固定方向
data Order (s :: Side) = Order
  { orderQuantity :: Int
  , orderPrice    :: Float
  } deriving (Eq, Show)

-- 提供类型安全的订单构造函数
buyOrder :: Int -> Float -> Order 'Buy
buyOrder = Order

sellOrder :: Int -> Float -> Order 'Sell
sellOrder = Order

-- Book类型明确指定各列表的订单类型
data Book = Book
  { buy  :: [Order 'Buy]
  , sell :: [Order 'Sell]
  } deriving (Show)

效果验证

在GHCi中尝试创建非法状态会直接触发编译错误:

ghci> let b = buyOrder 10 92.22
ghci> let s = sellOrder 20 93.0
ghci> Book [b] [s]  -- 合法,正常创建Book
Book {buy = [Order {orderQuantity = 10, orderPrice = 92.22}], sell = [Order {orderQuantity = 20, orderPrice = 93.0}]}

ghci> Book [s] []  -- 编译报错:无法将'Sell类型匹配为'Buy

方案二:使用智能构造函数(Smart Constructor)运行时约束

如果不想引入幽灵类型,可以通过隐藏Book的原始构造函数,仅提供安全的操作函数,在运行时检查订单方向。

代码实现

data Side = Buy | Sell deriving (Show, Eq)

data Order = Order
  { orderSide     :: Side
  , orderQuantity :: Int
  , orderPrice    :: Float
  } deriving (Eq, Show)

-- 模块导出时隐藏Book的构造函数,只暴露安全接口
module Book (Book, emptyBook, addBuyOrder, addSellOrder, getBuyOrders, getSellOrders) where

data Book = Book
  { buy  :: [Order]
  , sell :: [Order]
  } deriving (Show)

-- 空交易簿
emptyBook :: Book
emptyBook = Book [] []

-- 安全添加买单:仅接受Buy方向的订单
addBuyOrder :: Order -> Book -> Maybe Book
addBuyOrder o b
  | orderSide o == Buy = Just $ b { buy = o : buy b }
  | otherwise = Nothing

-- 安全添加卖单:仅接受Sell方向的订单
addSellOrder :: Order -> Book -> Maybe Book
addSellOrder o b
  | orderSide o == Sell = Just $ b { sell = o : sell b }
  | otherwise = Nothing

-- 获取订单列表的只读接口
getBuyOrders :: Book -> [Order]
getBuyOrders = buy

getSellOrders :: Book -> [Order]
getSellOrders = sell

效果验证

用户无法直接调用Book构造函数,必须通过addBuyOrder/addSellOrder操作,非法订单会返回Nothing:

ghci> let o = Order Sell 10 92.22
ghci> addBuyOrder o emptyBook  -- 返回Nothing,拒绝非法订单
Nothing
ghci> let b = Order Buy 5 92.0
ghci> addBuyOrder b emptyBook  -- 返回Just Book,创建合法状态
Just (Book {buy = [Order {orderSide = Buy, orderQuantity = 5, orderPrice = 92.0}], sell = []})

方案对比

  • 幽灵类型:编译期检查,零运行时开销,完全杜绝非法状态,是函数式编程的首选方案。
  • 智能构造函数:运行时检查,兼容性更好,但存在运行时开销,且无法在编译阶段发现错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:07:35