Арифметические операции на Хаскеле и постоянство в БД чисел произвольной / фиксированной точности

Как новичок на Haskell (платформа GHC), я столкнулся с проблемой работы с типами данных и арифметическими операциями, связанными с бизнес-областью, которая включает в себя операции с валютой / деньгами, и я ищу решение.

Я разрабатываю приложение, которое должно взаимодействовать с (независимым) бухгалтерским модулем (через веб-сервисы) и в то же время иметь (веб) пользовательский интерфейс для произвольного ввода данных, который хранится в отдельной базе данных (PostgreSQL).

Я пришел из среды C#/F# и System.Decimal покрывает все основные потребности там. Пожалуйста, исправьте меня, если я ошибаюсь, но в Haskell нет встроенного (по умолчанию) типа данных, который можно считать эквивалентным.

Идеальным выбором был бы тип данных, который предлагает арифметику произвольной точности или, по крайней мере, что-то в строках Decimal128 (IEEE 754). Тип должен поддерживать округление (до ближайшего, связывание от нуля и, если возможно, также до четного) и следующие операции: сложение, вычитание, умножение, деление (в идеале также квадрат / корень). Преобразования между типами также должны поддерживаться.

Из того, что мне удалось найти, есть два модуля Haskell на Hackage, которые должны точно выполнять вычисления - Data.Fixed и Data.Decimal (кстати, есть ли способ создания пользовательских литералов в Haskell - например, для копирования 123.45m от F#?). По крайней мере, последний, насколько я могу судить (после быстрого тестирования), позволяет большую часть того, что я описал в предыдущем абзаце, но когда я добавляю БД (PostgreSQL через Persistent/HDBC) и веб-фреймворк (YESOD) в смешанные вещи не выглядят такими привлекательными. Поддержка там, кажется, отсутствует.

Есть ли какая-либо другая комбинация, которая позволяет то, что я описал сквозным (ввод данных => обработка данных => хранение) с минимальным трением (например, ручное приведение из строки после загрузки из БД кажется странным, имея действительно строго типизированный язык) и без потери точности (любые указатели приветствуются)?

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
Другие вопросы по тегам