我写了一个玩具库,它使用依赖类型来表示类型签名中的货币货币:
data Currency = CHF | EUR | PLN | USD
deriving stock (Bounded, Enum, Eq, Read, Show)
data CurrencyWitness (c :: Currency) where
CHFWitness :: CurrencyWitness CHF
EURWitness :: CurrencyWitness EUR
PLNWitness :: CurrencyWitness PLN
USDWitness :: CurrencyWitness USD
deriving stock instance Eq (CurrencyWitness c)
deriving stock instance Show (CurrencyWitness c)
data Money (currency :: Currency) representation = Money
{ moneyCurrency :: !(CurrencyWitness currency)
, moneyAmount :: !representation
}
deriving stock (Eq, Show)
add :: (Num r) => Money c r -> Money c r -> Money c r
add (Money witness1 amount1) (Money _ amount2) =
Money witness1 (amount1 + amount2)
data SomeMoney r where
SomeMoney :: Money c r -> SomeMoney r
字符串
现在我如何编写addSomeMoney :: (Num r) => SomeMoney r -> SomeMoney r -> Maybe (SomeMoney r)
,只有当它们是相同的货币时才能安全地添加货币?你可以想象,在用户提供货币参数的情况下,这可能是需要的,所以我们不能在编译时检查他们的货币。
我最好的方法是:
addSomeMoney (SomeMoney m@(Money c _)) (SomeMoney m'@(Money c' _)) = case (c, c') of
(CHFWitness, CHFWitness) -> Just . SomeMoney $ add m m'
(PLNWitness, PLNWitness) -> Just . SomeMoney $ add m m'
-- ...
(_, _) -> Nothing
型
这种方法的缺点是编写起来很麻烦,重复性很强,并且当我添加新货币时会出错,因为编译器不会警告我没有处理这种情况。
addSomeMoney (SomeMoney m@(Money c _)) (SomeMoney m'@(Money c _)) = _
型
不工作。GHC抱怨重复的c
:Conlicting definitions of 'c'
。
2条答案
按热度按时间wmvff8tz1#
完全“类型安全”的方法是可能的,但是我们必须编写一些样板文件。
我们从OP的代码开始,大部分是逐字的,我们只添加了一些扩展,启用警告(应该在其他地方更合适,但让我们保持简单)和
import Data.Typeable
。(我没有检查是否可以删除某些扩展)
字符串
现在是新的部分。
我们首先“证明”任何货币类型
c
满足Typeable
约束,这很容易做到,但需要一些样板文件。重要的是,添加新的货币将触发非详尽的模式匹配警告,因此保持同步相当容易。
型
然后是货币添加函数。我们调用
withTypeableCurrency
将两个Typeable
约束放入作用域中。然后我们利用eqT
来检查两种货币类型是否相同。在肯定的情况下,我们不会得到无聊的True
布尔值,而是一个方便的Refl
,它允许编译器识别类型。剩下的就很容易了。型
这种方法的优点是样板文件被限制在
withTypeableCurrency
内,并且不必在处理SomeMoney
的每个函数中重复,如addSomeMoney
,subtractSomeMoney
等。更新:一些样板可以利用
singletons
包和一些Template Haskell删除。型
关键函数甚至更简单,利用
%~
。型
pcww981p2#
一种选择是编写一个函数,将单例降级回正常值:
字符串
然后你可以使用一个标准的相等性检查,和一个不经意的货币添加:
型
在这种情况下,
demoteCurrencyWitness
的遗忘子句将给予一个明确的错误,而不是错误的Nothing
结果。写这个还是有点乏味,所以我会用
singletons-th
代替。