Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 如何对货币、货币和在货币之间交换货币的银行进行建模?_Haskell_Types - Fatal编程技术网

Haskell 如何对货币、货币和在货币之间交换货币的银行进行建模?

Haskell 如何对货币、货币和在货币之间交换货币的银行进行建模?,haskell,types,Haskell,Types,嘿,我在读Java中的类型驱动开发。我很难找到Java类型,所以我尝试用Haskell编写它。然而,我有两个问题: 我不知道如何实现货币和实际货币之间的差异。起初,我认为货币只是货币的一种类型(我认为这是有道理的),比如data Dollar=Double,其中Dollar 4.0就是货币,而Dollar就是货币。我认为,Dollar::Double->Dollar不会被输出 这就导致了一个问题,我无法模拟一家兑换货币的银行。我在想类似于交换::(货币a,货币b)=>[ExchangeRate]

嘿,我在读Java中的类型驱动开发。我很难找到Java类型,所以我尝试用Haskell编写它。然而,我有两个问题:

  • 我不知道如何实现货币和实际货币之间的差异。起初,我认为货币只是货币的一种类型(我认为这是有道理的),比如
    data Dollar=Double
    ,其中
    Dollar 4.0
    就是货币,而
    Dollar
    就是货币。我认为,
    Dollar::Double->Dollar
    不会被输出
  • 这就导致了一个问题,我无法模拟一家兑换货币的银行。我在想类似于
    交换::(货币a,货币b)=>[ExchangeRate]->a->b
    。银行只是一个包含汇率集合的对象,但我不知道汇率是什么类型
  • 到目前为止,我掌握的代码是:

    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)
    不,不要使用类型类。让我们从基础开始:

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

    data CurrencyType = Dollar | Franc deriving (Show)
    
    data Money = Money {
          amount :: Double,
          mType :: CurrencyType
        } deriving (Show)
    
    要表示货币,请再次使用简单的数据类型:

    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}   
    
    λ> let fiveDollars = Money 5 (CurrencyType Dollar)
    λ> fiveDollars
    Money 5.0 (CurrencyType 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中的演示:

    *Main> let fiveDollars = Money 5 Dollar
    *Main> fiveDollars
    Money {amount = 5.0, mType = Dollar}   
    
    λ> let fiveDollars = Money 5 (CurrencyType Dollar)
    λ> fiveDollars
    Money 5.0 (CurrencyType Dollar)
    
    现在,假设您想定义另一种货币
    法郎
    。然后只需为其定义一个数据类型:

    data Franc = Franc deriving (Show)
    
    然后你可以从中定义金钱:

    λ> let fiveFranc = Money 5 (CurrencyType Franc)
    λ> fiveFranc
    Money 5.0 (CurrencyType Franc)
    
    >我无法编写编译时只需要美元的函数。

    嗯,你可以

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

    首先要注意的是,为了安全起见,
    exchange
    应该有

    exchange :: (Money a, Money b) => [ExchangeRate] -> a -> Maybe b
    
    因为如果您的费率列表中没有
    a
    b
    ,您将无法返回任何内容

    对于
    ExchangeRate
    ,我们可以使用:

    newtype ExchangeRate = Rate { unrate :: (TypeRep, Double) }
      deriving Show
    
    TypeRep
    是类型的唯一“指纹”。您可以通过调用带有
    Typeable
    实例的东西上的
    typeOf
    来获得
    TypeRep
    。使用该类,我们可以编写汇率的类型安全查找:

    findRate::Typeable a=>[ExchangeRate]->a->可能是双精度的
    findRate速率a=查找(类型a)(映射未速率)
    
    然后我们可以实现您的交换功能:

    convertMoney :: CurrencyType -> Money -> Money
    convertMoney Dollar money = undefined -- logic for Converting money to Dollar                
    convertMoney Franc money = undefined -- logic for converting money to Franc  
    
    exchange::for all a b。(货币a,货币b)=>[汇率]->a->可能是b
    汇率a=do
    
    aRate我将如何在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
    

    我倾向于将货币视为衡量单位,并以F#风格行事。另外,不要使用浮点。但我发现这有两个问题。1、其他人不能声明他们自己的货币类型(尽管我不确定这有多大问题)。更重要的是,我不能编写一个在编译时只需要美元的函数。这就增加了一整类错误。@RamithJayatilleka我更新了我的答案,以包括您的其他用例。好的,谢谢。在看了Gallais的答案和链接后,我明白了我应该通过虚拟货币来赚钱。这现在是有道理的。@RamithJayatilleka:这是一个好方法。然而,当你想要一套动态的汇率时,你会遇到一些困难。(就像你的
    [ExchangeRate]
    )我现在正在写一个答案来处理它,但有点尴尬。@TikhonJelvis是的,我刚刚碰到了那个问题。我尝试了
    data Rate a b=(货币a,货币b)=>Rate Double
    ,因为我认为在类型中编码货币是有意义的。但我不知道如何在货币不同的情况下对汇率集合进行编码。*嘿,我从库拉认识你!哇,我不知道有可打字的东西存在。谢谢。使用像
    Double
    这样的浮点表示法可能会导致一些严重的财务问题(浮点错误可能会导致非常重要的不准确,这肯定会在一系列操作中累积)。我建议改为使用定点表示法,这样您就可以精确地将数字表示到美分(如果用例需要的话,也可以是美分的一小部分)。至少有一个关于定点算术的黑客软件包(我相信还有更多)。