嘿所以我正在阅读关于Java中类型驱动开发的这篇文章.我无法学习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
Run Code Online (Sandbox Code Playgroud)
编辑:我仍然想要这样的安全:buyAmericanBigMac :: Dollar -> (BigMac, Dollar).
cch*_*ers 20
首先要注意,为了安全起见,exchange应该有类型
exchange :: (Money a, Money b) => [ExchangeRate] -> a -> Maybe b
Run Code Online (Sandbox Code Playgroud)
因为如果您没有a或b在您的费率列表中,您将无法返回任何内容.
因为ExchangeRate我们可以使用:
newtype ExchangeRate = Rate { unrate :: (TypeRep, Double) }
deriving Show
Run Code Online (Sandbox Code Playgroud)
这TypeRep是一种独特的"指纹".你可以得到一个TypeRep通过调用typeOf上的东西用Typeable实例.使用这个类,我们可以编写一个类型安全的汇率查找:
findRate :: Typeable a => [ExchangeRate] -> a -> Maybe Double
findRate rates a = lookup (typeOf a) (map unrate rates)
Run Code Online (Sandbox Code Playgroud)
然后我们可以实现你的交换功能:
exchange :: forall a b. (Money a, Money b) => [ExchangeRate] -> a -> Maybe b
exchange rates a = do
aRate <- findRate rates a
bRate <- findRate rates (undefined :: b)
return $ money (bRate * (amount a / aRate))
Run Code Online (Sandbox Code Playgroud)
在这里我们使用ScopedTypeVariables扩展,所以我们可以写undefined :: b(注意我们需要写forall a b.这个以便工作)
这是一个最小的工作示例.而不是[ExchangeRate]我使用了HashMap(它更快,并阻止用户组合不属于一起的交换率).
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Exchange
( Dollar
, Franc
, exchange
, sampleRates
, sampleDollars
) where
import Data.HashMap.Strict as HM
import Data.Typeable
class Typeable m => 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
newtype Dollar = Dollar Double
deriving (Show, Eq, Typeable)
instance Money Dollar where
money = Dollar
amount (Dollar a) = a
newtype Franc = Franc Double
deriving (Show, Eq, Typeable)
instance Money Franc where
money = Franc
amount (Franc a) = a
newtype ExchangeRates = Exchange (HashMap TypeRep Double)
deriving Show
findRate :: Typeable a => ExchangeRates -> a -> Maybe Double
findRate (Exchange m) a = HM.lookup (typeOf a) m
exchange :: forall a b. (Money a, Money b) => ExchangeRates -> a -> Maybe b
exchange rates a = do
aRate <- findRate rates a
bRate <- findRate rates (undefined :: b)
return $ money (bRate * (amount a / aRate))
sampleRates :: ExchangeRates
sampleRates = Exchange $ HM.fromList
[ (typeOf (Dollar 0), 1)
, (typeOf (Franc 0) , 1.2)
]
sampleDollars :: Dollar
sampleDollars = Dollar 5
Run Code Online (Sandbox Code Playgroud)
然后你就可以写了
> exchange sampleRates sampleDollars :: Maybe Franc
Just (Franc 6.0)
Run Code Online (Sandbox Code Playgroud)
正如其他人提到的那样,Double因为你可以得到浮点错误,所以不太合适.如果你用真钱做任何事,我建议使用科学.
不,不要使用类型类.让我们从基础开始:
那么,您想表示不同的货币类型?让我们使用一个简单的代数数据类型:
data CurrencyType = Dollar | Franc deriving (Show)
Run Code Online (Sandbox Code Playgroud)
你想代表钱,再次使用简单的数据类型:
data Money = Money {
amount :: Double,
mType :: CurrencyType
} deriving (Show)
Run Code Online (Sandbox Code Playgroud)
ghci中的一些演示:
*Main> let fiveDollars = Money 5 Dollar
*Main> fiveDollars
Money {amount = 5.0, mType = Dollar}
Run Code Online (Sandbox Code Playgroud)
现在,您希望能够将货币从一种货币类型转换为另一种货币类型.这又可以通过一个简单的功能来实现:
convertMoney :: CurrencyType -> Money -> Money
convertMoney Dollar money = undefined -- logic for Converting money to Dollar
convertMoney Franc money = undefined -- logic for converting money to Franc
Run Code Online (Sandbox Code Playgroud)
进入类型类的一般规则是当我想要表示一些具有明确定义的法则的特定抽象时.对于大多数情况,简单的数据类型和在它们上运行的函数将是一个很好的例子.
根据您的评论进行更新:如果您希望能够声明自己的资金类型,那么您可以遵循以下方法:
data CurrencyType a = CurrencyType a deriving (Show)
data Dollar = Dollar deriving (Show)
data Money a = Money Double (CurrencyType a) deriving (Show)
Run Code Online (Sandbox Code Playgroud)
演示ghci:
?> let fiveDollars = Money 5 (CurrencyType Dollar)
?> fiveDollars
Money 5.0 (CurrencyType Dollar)
Run Code Online (Sandbox Code Playgroud)
现在假设你要定义另一种货币Franc.然后只为它定义一个数据类型:
data Franc = Franc deriving (Show)
Run Code Online (Sandbox Code Playgroud)
然后你可以用它来定义钱:
?> let fiveFranc = Money 5 (CurrencyType Franc)
?> fiveFranc
Money 5.0 (CurrencyType Franc)
Run Code Online (Sandbox Code Playgroud)
>> 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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2404 次 |
| 最近记录: |