我正在处理货币和货币操作。我希望操作是类型安全的,但我还需要将不同的货币一起存储在一个集合中,以便我可以搜索它们。
这两个目标似乎发生了冲突。
我可以使用选项类型来实现它,但在操作中我没有获得类型安全:
type Number = Rational
data Currency = USD | EUR | GBP
data Value = Value Number Currency
-- I can have this
type ConversionRate = (Currency, Currency, Number)
conversionRates :: [ConversionRate]
conversionRates = [(GBP, EUR, 1.2)]
-- This is not typesafe and would allow summing different currencies
sumValue :: Value -> Value -> Value
sumValue = undefined
-- This is also not typesafe
convert :: ConversionRate -> Value -> Currency -> Maybe Value
convert = undefined
或者我可以为每种货币使用一种类型,但我不能轻松地创建和处理它们的汇率。
{-# LANGUAGE GADTSyntax #-}
{-# LANGUAGE ExistentialQuantification #-}
type Number = Rational
data USD = USD
data EUR = EUR
data GBP = GBP
class Currency a
instance Currency USD
instance Currency EUR
instance Currency GBP
data Value a where
Value :: Currency a => a -> Value a
data ConversionRate a b where
ConversionRate :: (Currency a, Currency b) => Number -> ConversionRate a b
-- Now I can have type-safe currency operations
sumValue :: Currency a => Value a -> Value a
sumValue = undefined
-- And I can make sure my conversions make sense
convert :: ConversionRate a b -> Value a -> b
convert = undefined
-- But I can't hold a list of conversion rates that I can easily manipulate
type ConversionRates = ??
我现在是怎么做的
我目前的解决方案是货币作为不同类型和货币期权类型之间的同构,希望在程序的不同部分中拥有两全其美的优势。但这是一团糟。
{-# LANGUAGE ExistentialQuantification #-}
type Number = Rational
data Symbol = USD | EUR | GBP
data Dollar = Dollar
data Euro = Euro
data Pound = Pound
class Currency a where
toSymbol :: a -> Symbol
instance Currency Dollar where toSymbol _ = USD
instance Currency Euro where toSymbol _ = EUR
instance Currency Pound where toSymbol _ = GBP
data Wrapper = forall a. Currency a => Wrapper a
toCurrency :: Symbol -> Wrapper
我怎样才能在某些函数中具有类型安全性以及在其他函数中具有相同类型值的便利性?. 看起来像是一份工作,DataKinds
但我看不出它有什么帮助。
请记住,我在编码时没有所有数据。它将从 API 中获取。