嘿所以我正在阅读关于Java中类型驱动开发的this post。我无法学习Java类型,所以我尝试在Haskell中编写它。但是,我有两个问题:
data Dollar = Dollar Double
,其中像Dollar 4.0
这样的值是金钱,Dollar
类型是货币。我认为Dollar :: Double -> Dollar
将不会被导出。exchange :: (Money a, Money b) =>[ExchangeRate] -> a -> b
之类的东西。然后银行只是一个包含ExchangeRates集合的对象,但我不知道ExchangeRate的类型。我到目前为止的代码是:
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)
。
答案 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