我理解函数式语言中“使非法状态不可表示”的原则,但在实践中我经常遇到困难。
作为一个例子,我正在尝试定义一个交易簿模型。我定义了这些数据类型:
data Side = Buy | Sell
deriving (Show, Eq)
data Order =
Order
{
orderSide :: Side
, orderQuantity :: Int
, orderPrice :: Float
}
deriving (Eq)
data Book =
Book
{ buy :: [Order]
, sell :: [Order]
}
deriving (Show)
Run Code Online (Sandbox Code Playgroud)
基本上,这意味着 aBook是一种具有两个订单列表的类型,每侧一个。
然而,这是完全有效的:
ghci> o = Order Sell 10 92.22
ghci> Book [o] []
Book {buy = [Order {orderSide = Sell, orderQuantity = 10, orderPrice = 92.22}], sell = []}
Run Code Online (Sandbox Code Playgroud)
这也是完全错误的。
我如何表达只有Buy订单应该发送给买方而Sell订单发送给另一方的约束?
您可以通过多种不同的方式来做到这一点,但我认为这是最简单的:
data OrderInfo =
OrderInfo
{
orderQuantity :: Int
, orderPrice :: Float
}
deriving (Eq)
data BuyOrder = BuyOrder OrderInfo
data SellOrder = SellOrder OrderInfo
data Book =
Book
{ buy :: [BuyOrder]
, sell :: [SellOrder]
}
deriving (Show)
Run Code Online (Sandbox Code Playgroud)
不幸的是,这使得编写适用于卖出和买入订单的通用函数变得不可能。您可以通过引入另一种类型和一些简单的转换函数来恢复这种能力:
data Side = Buy | Sell
deriving (Show, Eq)
data SomeOrder = SomeOrder Side OrderInfo
fromBuy :: BuyOrder -> SomeOrder
fromSell :: SellOrder -> SomeOrder
buyOrSell :: SomeOrder -> Either BuyOrder SellOrder
Run Code Online (Sandbox Code Playgroud)
您可以通过使用 GADT 和 DataKind 来避免这种开销:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
data Side = Buy | Sell
deriving (Show, Eq)
data SSide a where
SBuy :: SSide Buy
SSell :: SSide Sell
deriving instance Show (SSide a)
deriving instance Eq (SSide a)
data Order side =
Order
{
orderSide :: SSide side
, orderQuantity :: Int
, orderPrice :: Float
} deriving (Show, Eq)
data Book =
Book
{ buy :: [Order Buy]
, sell :: [Order Sell]
}
deriving Show
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
76 次 |
| 最近记录: |