如何模拟货币之间兑换货币的货币,货币和银行?

时间:2014-12-10 19:04:23

标签: haskell types

嘿所以我正在阅读关于Java中类型驱动开发的this post。我无法学习Java类型,所以我尝试在Haskell中编写它。但是,我有两个问题:

  1. 我不知道如何实现货币与实际货币之间的差异。起初我认为货币只是货币的类型(我认为这是有意义的),例如data Dollar = Dollar Double,其中像Dollar 4.0这样的值是金钱,Dollar类型是货币。我认为Dollar :: Double -> Dollar将不会被导出。
  2. 这导致了一个问题,即我无法为交换金钱的银行建模。我在想exchange :: (Money a, Money b) =>[ExchangeRate] -> a -> b之类的东西。然后银行只是一个包含ExchangeRates集合的对象,但我不知道ExchangeRate的类型。
  3. 我到目前为止的代码是:

    class Money m where
        money :: (Money m) => Double -> m
        amount :: (Money m) => m -> Double
        add :: (Money m) => m -> m -> m
        add a b = money $ amount a + amount b
    
    class (Money a, Money b) => ExchangeablePair a b where
    
    newtype Dollar = Dollar Double
                     deriving (Show, Eq)
    
    instance Money Dollar where
        money = Dollar
        amount (Dollar a) = a
    
    newtype Franc = Franc Double
                     deriving (Show, Eq)
    
    instance Money Franc where
        money = Franc
        amount (Franc a) = a
    
    instance ExchangeablePair Dollar Franc where
    

    编辑:我仍然想要这样的安全:buyAmericanBigMac :: Dollar -> (BigMac, Dollar)

2 个答案:

答案 0 :(得分:7)

不,不要使用类型类。让我们从基础开始:

那么,您想表示不同的货币类型?让我们使用一个简单的代数数据类型:

data CurrencyType = Dollar | Franc deriving (Show)

您想要代表金钱,再次使用简单的数据类型:

data Money = Money {
      amount :: Double,
      mType :: CurrencyType
    } deriving (Show)

ghci中的一些演示:

*Main> let fiveDollars = Money 5 Dollar
*Main> fiveDollars
Money {amount = 5.0, mType = Dollar}   

现在,您希望能够将货币从一种货币类型转换为 另一个。这可以通过一个简单的函数来实现:

convertMoney :: CurrencyType -> Money -> Money
convertMoney Dollar money = undefined -- logic for Converting money to Dollar                
convertMoney Franc money = undefined -- logic for converting money to Franc  

我转向类型类的一般规则是当我想要表示一些具有明确定义的法则的特定抽象时。对于大多数情况,简单的数据类型和在它们上运行的函数将是一个很好的例子。


根据您的评论

更新:如果您希望能够宣布自己的资金类型,那么您可以采用以下方法:

data CurrencyType a = CurrencyType a deriving (Show)

data Dollar = Dollar deriving (Show)

data Money a = Money Double (CurrencyType a) deriving (Show)

ghci中的演示:

λ> let fiveDollars = Money 5 (CurrencyType Dollar)
λ> fiveDollars
Money 5.0 (CurrencyType Dollar)

现在让我们假设您要定义另一种货币Franc。然后只为它定义一个数据类型:

data Franc = Franc deriving (Show)

然后你可以用它来定义钱:

λ> let fiveFranc = Money 5 (CurrencyType Franc)
λ> fiveFranc
Money 5.0 (CurrencyType Franc)

>> I can't write a function that only takes Dollars at compile time.

嗯,你可以。

convertFromDollar :: Money Dollar -> Money Franc
convertFromDollar x = undefined -- Write your logic here     

答案 1 :(得分:0)

我将如何在Haskell中根据我在PHP中的工作方式实现它:

module Money where


-- For instance Show Money
import Text.Printf


-- Should perhaps be some Decimal type
type Amount = Double


-- Currency type
data Currency = Currency { iso4217 :: String } deriving Eq

instance Show Currency where
    show c = iso4217 c


-- Money type
data Money = Money { amount :: Amount, currency :: Currency }

instance Show Money where
    show m = printf "%0.2f" (amount m) ++ " " ++ show (currency m)


-- Conversion between currencies
data BasedRates = BasedRates { base :: Currency, rate :: Currency -> Amount }

type CrossRates = Currency -> Currency -> Amount

makeCrossRatesFromBasedRates :: BasedRates -> CrossRates
makeCrossRatesFromBasedRates (BasedRates { base=base, rate=rate }) = 
    \ fromCurrency toCurrency -> rate toCurrency / rate fromCurrency


convert :: CrossRates -> Currency -> Money -> Money
convert crossRates toCurrency (Money { amount=amount, currency=fromCurrency }) 
    = Money { amount = crossRates fromCurrency toCurrency * amount, currency=toCurrency }



-- Examples

sek = Currency { iso4217 = "SEK" }
usd = Currency { iso4217 = "USD" }
eur = Currency { iso4217 = "EUR" }

sekBasedRates = BasedRates {
    base = sek,
    rate = \currency -> case currency of
        Currency { iso4217 = "SEK" } -> 1.0000
        Currency { iso4217 = "USD" } -> 6.5432
        Currency { iso4217 = "EUR" } -> 9.8765
}

crossRates = makeCrossRatesFromBasedRates sekBasedRates

usdPrice = Money { amount = 23.45, currency = usd }
sekPrice = convert crossRates sek usdPrice
eurPrice = convert crossRates eur usdPrice