Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell arithmetic operations and DB persistence of arbitrary/fixed precision numbers

As a Haskell (GHC platform) beginner I bumped into a problem of dealing with data types and arithmetic operations related to business domain that involves currency/money operations and I'm looking for a solution.

I'm developing application that is supposed to interface with (independent) accounting module (via web services) while at the same time having a (web) user interface for ad-hoc data entry which is stored in a separate database (PostgreSQL).

I'm coming from C#/F# environment and System.Decimal covers all the core needs there. Please correct me if I'm wrong but Haskell does not seem to have an integrated (default) data type that could be considered equivalent.

Ideal choice would be a data type that offers arbitrary precision arithmetic or at least something in the lines of Decimal128 (IEEE 754). The type should support rounding (to nearest, ties away from zero and if possible also ties to even) and the following operations: add, subtract, multiply, divide (ideally also square/root). Conversions between types should also be supported.

From what I've managed to find there are two Haskell modules on Hackage that are supposed to perform calculations exactly - Data.Fixed and Data.Decimal (by the way is there any way of creating custom literals in Haskell - e.g. to copy 123.45m from F# ?). At least the latter would as far as I can tell (after a quick test) enables most of what I've described in previous paragraph, but when I add a DB (PostgreSQL via Persistent/HDBC) and a web framework (YESOD) to the mix things don't look so peachy. The support there seems to be lacking.

Is there any other combination that enables what I've described end-to-end (data entry => data processing => storage) with minimal friction (e.g. manual casting from string after loading from DB seems odd having a really strongly typed language) and without the loss of precision (any pointers welcome)?

like image 661
CMZ3Z3G6P3 Avatar asked Aug 30 '12 17:08

CMZ3Z3G6P3


1 Answers

I am making a Yesod application, and using Database.Persist. For my purposes, I'm going to make a newtype wrapping over a Data.Fixed value, and use an Int64 field, like so:

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
like image 129
nomen Avatar answered Sep 30 '22 11:09

nomen