Haskell 算术运算和任意/固定精度数的 DB 持久性
Posted
技术标签:
【中文标题】Haskell 算术运算和任意/固定精度数的 DB 持久性【英文标题】:Haskell arithmetic operations and DB persistence of arbitrary/fixed precision numbers 【发布时间】:2012-08-30 17:10:55 【问题描述】:作为一名 Haskell(GHC 平台)初学者,我遇到了处理与涉及货币/货币操作的业务领域相关的数据类型和算术运算的问题,我正在寻找解决方案。
我正在开发应该与(独立)会计模块(通过 Web 服务)交互的应用程序,同时具有用于存储在单独数据库中的临时数据输入的(Web)用户界面( PostgreSQL)。
我来自 C#/F# 环境,System.Decimal 涵盖了那里的所有核心需求。如果我错了,请纠正我,但 Haskell 似乎没有可以被视为等效的集成(默认)数据类型。
理想的选择是提供任意精度算术或至少符合 Decimal128 (IEEE 754) 的数据类型。该类型应支持舍入(最接近,远离零,如果可能还与偶数相关)和以下操作:加法、减法、乘法、除法(理想情况下也是平方/根)。还应该支持类型之间的转换。
据我所知,Hackage 上有两个 Haskell 模块应该精确地执行计算 - Data.Fixed 和 Data.Decimal (顺便说一句,有什么方法可以在 Haskell 中创建自定义文字 - 例如从 F# 复制 123.45m?)。至少后者会据我所知(在快速测试之后)启用我在上一段中描述的大部分内容,但是当我添加一个数据库(通过 Persistent/HDBC 的 PostgreSQL)和一个 Web 框架(YESOD)时混合的东西看起来不那么桃色。那里似乎缺乏支持。
是否有任何其他组合可以使我所描述的端到端(数据输入 => 数据处理 => 存储)以最小的摩擦(例如,从数据库加载后从字符串手动转换似乎很奇怪,具有非常强类型语言)并且不损失精度(欢迎任何指针)?
【问题讨论】:
您不需要自定义文字,小数文字123.45
代表fromRational (2469 % 20)
,调用哪个fromRational
由类型决定。类型由上下文(可能是类型签名)确定。由于Rational
准确处理有理数(在机器和 GMP 规定的内存限制内),因此不需要类型文字。
我发现自定义文字使用起来不那么麻烦,而且更美观 - 导致代码更易于阅读 - 第一次看到它我相信理解 123.45m 比 12345 % 100 更容易, 2469 % 20 更难与定义的业务规则联系起来(但我想这可能只是个人喜好的问题,尤其是在习惯了一段时间之后)。
不,您只需使用文字 123.45
。转换为有理数,然后转换为所需类型的值是由实现完成的。你不需要为此烦恼。 123.45m
形式的文字唯一要添加的是代码的人类读者可以立即看到类型 - 如果他知道后缀的含义。
不,这就是重点,在 Haskell 中,这意味着隐式使用 Rational
,而不是 Double
。
@CMZ3Z3G6P3:这将是一个“默认”问题——您上面的let a = ...
定义了一个多态值,但实际上只能评估具体类型。如果没有任何限制这种表达式的类型,它确实会默认为Double
。 Haskell 在数字类型之间没有任何隐式转换,因此单个具体类型将消除它涉及的每个计算的歧义,即使是间接的。因此,GHCi 提示符通常是您看到类型默认发生的唯一地方。
【参考方案1】:
我正在制作一个 Yesod 应用程序,并使用 Database.Persist。出于我的目的,我将创建一个包装 Data.Fixed 值的新类型,并使用 Int64 字段,如下所示:
newtype Dollars = Dollars unDollars :: Centi deriving (Eq, Num)
instance PersistField Dollars where
toPersistValue = PersistInt64 . fromIntegral . fromEnum . unDollars
fromPersistValue (PersistInt64 c) = Right . Dollars . toEnum . fromIntegral $ c
fromPersistValue x = Left . Text.pack $ "Expected Int64 counting cents, got: " ++ show x
【讨论】:
以上是关于Haskell 算术运算和任意/固定精度数的 DB 持久性的主要内容,如果未能解决你的问题,请参考以下文章