gpt4 book ai didi

haskell - 尝试构建一个处理不同货币金额的应用实例,我是否走错了路?

转载 作者:行者123 更新时间:2023-12-02 12:02:52 25 4
gpt4 key购买 nike

我正在再次尝试 Haskell(阅读很棒的 learnyouahaskell 教程),并且我正在拼命寻找一颗钉子来应用这把伟大的锤子。

我的日常工作是电子商务,我们有大量操纵价格的软件设计问题。所以我想,使用 Haskell 类型系统来抽象出诸如货币之间转换或税前/税后价格之类的东西,同时保证正确性可能会很好。

这是我想要实现的目标的粗略想法(显然不是编译,否则不会在这里):

data Money a = Amount String a | ConversionError
deriving Show

convert :: String -> Money Double -> Money Double
convert toCurrency (Amount fromCurrency 0) = Amount toCurrency 0
convert "EUR" (Amount "USD" x) = Amount "EUR" (0.92 * x)
convert "USD" (Amount "EUR" x) = Amount "USD" (1.09 * x)
convert _ _ = ConversionError

instance Functor Money where
fmap f (Amount currency number) = Amount currency (f number)

instance Applicative Money where
ConversionError <*> _ = ConversionError
Amount x f <*> amount = let Amount _ n = convert x amount
in
Amount x (f n)
pure = Amount "EUR"

eur5 = Amount "EUR" 5
usd5 = Amount "USD" 5


main = do
print $ fmap (+1) eur5 -- Amount "EUR" 6
print $ convert "EUR" usd5 -- Amount "EUR" 4.6000000000000005
print $ (+) <$> eur5 <*> usd5 -- I'd like it to print: Amount "EUR" 9.58

这不会编译,因为:

Couldn't match type ‘a’ with ‘Double’
‘a’ is a rigid type variable bound by
the type signature for
(<*>) :: Money (a -> b) -> Money a -> Money b
at src/Main.hs:14:21
Expected type: Money Double
Actual type: Money a

我明白为什么。

我认为一个可能的解决办法是让 Money 类型携带转换函数,但这使整个事情不太优雅,我必须这样声明我的价格:

data Money a = Amount (String -> (Money a) -> (Money a)) a
eur5 = Amount convert "EUR" 5

我真正想要的是,如果我可以仅在计算发生时才开始担心转换,而不需要将转换函数嵌入到 Money 值中。

那么,我是否走在正确的轨道上,但误解了某些东西,或者 Applicative 真的不是简化此类问题的解决方案?

最佳答案

is Applicative really not a solution to simplify this sort of problem?

是的,事实并非如此。正如您已经说过的,问题在于

(<*>) :: Applicative f => f (a -> b) -> f a -> f b

需要与任何 a 配合使用和b ,但您只允许 a ~ Double , b ~ Double

但是,如果我们想到任何 Applicative f作为一个知道如何应用函数的容器,存储某种 Money 似乎很奇怪在那里。另外,看来您只想要 Applicative (+) <$> a <*> b 的实例,这是一种误用。相反,编写一个小助手:

($+) :: Money a -> Money a -> Money a
(Amount c a) $+ (Amount c' b) = ...

这也为您以后的更改提供了更多的权力。例如,您可以使用幻像类型而不是当前的方法来用货币标记 Money:

newtype Money c a = Amount a deriving Show

data USD
data EUR

usd :: Num a => a -> Money USD a
usd = Amount

eur :: Num a => a -> Money EUR a
eur = Amount

($+) :: Money c a -> Money c a -> Money c a
(Amount a) $+ (Amount b) = Amount $ a + b

-- eur 5 $+ usd 4 = type error

但是,请记住,货币对于编译时转换来说是一种相当糟糕的类型,因为汇率一直在变化。更现实的方法是

($+) :: Fractional a => CurrencyEnv -> Money a -> Money a -> Maybe (Money a)

在哪里加载 CurrencyEnv在你的程序的开始。请记住,您不应该使用 Double对于与货币相关的任务,除非您想向老板解释为什么您缺少一些便士。 Data.Ratio.Rational更适合。

关于haskell - 尝试构建一个处理不同货币金额的应用实例,我是否走错了路?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34827456/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com