-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | A library providing definitions to work with monetary values. -- -- Please see the README on GitHub at -- https://github.com/telostat/haspara#readme @package haspara @version 0.0.0.0 -- | This module provides internal definitions for modeling and working -- with currencies. module Haspara.Internal.Currency -- | Type encoding for currencies. newtype Currency MkCurrency :: Text -> Currency [currencyCode] :: Currency -> Text -- | Smart constructor for Currency values within MonadError -- context. -- --
-- >>> currency "" :: Either String Currency -- Left "Currency code error! Expecting at least 3 uppercase characters, but received: \"\"" -- -- >>> currency " " :: Either String Currency -- Left "Currency code error! Expecting at least 3 uppercase characters, but received: \" \"" -- -- >>> currency "AB" :: Either String Currency -- Left "Currency code error! Expecting at least 3 uppercase characters, but received: \"AB\"" -- -- >>> currency " ABC " :: Either String Currency -- Left "Currency code error! Expecting at least 3 uppercase characters, but received: \" ABC \"" -- -- >>> currency "ABC" :: Either String Currency -- Right ABC --currency :: MonadError String m => Text -> m Currency -- | Smart constructor for Currency values within MonadFail -- context. -- --
-- >>> currencyFail "" :: Maybe Currency -- Nothing -- -- >>> currencyFail "US" :: Maybe Currency -- Nothing -- -- >>> currencyFail "usd" :: Maybe Currency -- Nothing -- -- >>> currencyFail "USD" :: Maybe Currency -- Just USD --currencyFail :: MonadFail m => Text -> m Currency -- | Parser that parses currency codes. -- --
-- >>> MP.runParser currencyCodeParser "Example" ""
-- Left (ParseErrorBundle {bundleErrors = TrivialError 0 (Just EndOfInput) (fromList []) :| [], bundlePosState = PosState {pstateInput = "", pstateOffset = 0, pstateSourcePos = SourcePos {sourceName = "Example", sourceLine = Pos 1, sourceColumn = Pos 1}, pstateTabWidth = Pos 8, pstateLinePrefix = ""}})
--
-- >>> MP.runParser currencyCodeParser "Example" " "
-- Left (ParseErrorBundle {bundleErrors = TrivialError 0 (Just (Tokens (' ' :| ""))) (fromList []) :| [], bundlePosState = PosState {pstateInput = " ", pstateOffset = 0, pstateSourcePos = SourcePos {sourceName = "Example", sourceLine = Pos 1, sourceColumn = Pos 1}, pstateTabWidth = Pos 8, pstateLinePrefix = ""}})
--
-- >>> MP.runParser currencyCodeParser "Example" "a"
-- Left (ParseErrorBundle {bundleErrors = TrivialError 0 (Just (Tokens ('a' :| ""))) (fromList []) :| [], bundlePosState = PosState {pstateInput = "a", pstateOffset = 0, pstateSourcePos = SourcePos {sourceName = "Example", sourceLine = Pos 1, sourceColumn = Pos 1}, pstateTabWidth = Pos 8, pstateLinePrefix = ""}})
--
-- >>> MP.runParser currencyCodeParser "Example" "A"
-- Left (ParseErrorBundle {bundleErrors = TrivialError 1 (Just EndOfInput) (fromList []) :| [], bundlePosState = PosState {pstateInput = "A", pstateOffset = 0, pstateSourcePos = SourcePos {sourceName = "Example", sourceLine = Pos 1, sourceColumn = Pos 1}, pstateTabWidth = Pos 8, pstateLinePrefix = ""}})
--
-- >>> MP.runParser currencyCodeParser "Example" "AB"
-- Left (ParseErrorBundle {bundleErrors = TrivialError 2 (Just EndOfInput) (fromList []) :| [], bundlePosState = PosState {pstateInput = "AB", pstateOffset = 0, pstateSourcePos = SourcePos {sourceName = "Example", sourceLine = Pos 1, sourceColumn = Pos 1}, pstateTabWidth = Pos 8, pstateLinePrefix = ""}})
--
-- >>> MP.runParser currencyCodeParser "Example" "ABC"
-- Right "ABC"
--
currencyCodeParser :: Parsec Void Text Text
newtype CurrencyPair
MkCurrencyPair :: (Currency, Currency) -> CurrencyPair
[unCurrencyPair] :: CurrencyPair -> (Currency, Currency)
toTuple :: CurrencyPair -> (Currency, Currency)
baseCurrency :: CurrencyPair -> Currency
quoteCurrency :: CurrencyPair -> Currency
currencyPair :: MonadError String m => Currency -> Currency -> m CurrencyPair
currencyPairFail :: MonadFail m => Currency -> Currency -> m CurrencyPair
instance Language.Haskell.TH.Syntax.Lift Haspara.Internal.Currency.Currency
instance GHC.Classes.Ord Haspara.Internal.Currency.Currency
instance Data.Hashable.Class.Hashable Haspara.Internal.Currency.Currency
instance GHC.Classes.Eq Haspara.Internal.Currency.Currency
instance Language.Haskell.TH.Syntax.Lift Haspara.Internal.Currency.CurrencyPair
instance GHC.Classes.Ord Haspara.Internal.Currency.CurrencyPair
instance Data.Hashable.Class.Hashable Haspara.Internal.Currency.CurrencyPair
instance GHC.Classes.Eq Haspara.Internal.Currency.CurrencyPair
instance GHC.Show.Show Haspara.Internal.Currency.CurrencyPair
instance GHC.Show.Show Haspara.Internal.Currency.Currency
instance Data.String.IsString Haspara.Internal.Currency.Currency
instance Data.Aeson.Types.FromJSON.FromJSON Haspara.Internal.Currency.Currency
instance Data.Aeson.Types.ToJSON.ToJSON Haspara.Internal.Currency.Currency
-- | This module provides base data definitions and functions for
-- Haspara library.
module Haspara.Currency
-- | Type encoding for currencies.
data Currency
currencyCode :: Currency -> Text
-- | Smart constructor for Currency values within MonadError
-- context.
--
-- -- >>> currency "" :: Either String Currency -- Left "Currency code error! Expecting at least 3 uppercase characters, but received: \"\"" -- -- >>> currency " " :: Either String Currency -- Left "Currency code error! Expecting at least 3 uppercase characters, but received: \" \"" -- -- >>> currency "AB" :: Either String Currency -- Left "Currency code error! Expecting at least 3 uppercase characters, but received: \"AB\"" -- -- >>> currency " ABC " :: Either String Currency -- Left "Currency code error! Expecting at least 3 uppercase characters, but received: \" ABC \"" -- -- >>> currency "ABC" :: Either String Currency -- Right ABC --currency :: MonadError String m => Text -> m Currency -- | Smart constructor for Currency values within MonadFail -- context. -- --
-- >>> currencyFail "" :: Maybe Currency -- Nothing -- -- >>> currencyFail "US" :: Maybe Currency -- Nothing -- -- >>> currencyFail "usd" :: Maybe Currency -- Nothing -- -- >>> currencyFail "USD" :: Maybe Currency -- Just USD --currencyFail :: MonadFail m => Text -> m Currency data CurrencyPair toTuple :: CurrencyPair -> (Currency, Currency) baseCurrency :: CurrencyPair -> Currency quoteCurrency :: CurrencyPair -> Currency currencyPair :: MonadError String m => Currency -> Currency -> m CurrencyPair currencyPairFail :: MonadFail m => Currency -> Currency -> m CurrencyPair -- | This module provides data definitions and functions for date values. module Haspara.Internal.Date -- | Type encoding for date values. -- -- This is a convenience wrapper around Day type. It helps us to -- avoid defining orphan instances. newtype Date MkDate :: Day -> Date -- | Builds a Date from a given Day. -- --
-- >>> fromDay (read "2021-01-01") -- 2021-01-01 --fromDay :: Day -> Date -- | Builds a Date from a given year, month and day as in Gregorian -- calendar. -- --
-- >>> fromYMD 2021 1 1 -- 2021-01-01 --fromYMD :: Integer -> Int -> Int -> Date -- | Attempts to parse and return Date from a given String -- with ISO format. -- --
-- >>> fromString "2021-01-01" :: Maybe Date -- Just 2021-01-01 -- -- >>> fromString "20210101" :: Maybe Date -- Nothing --fromString :: MonadFail m => String -> m Date -- | Attempts to parse and return Date from a given String -- with given date format. -- --
-- >>> fromFormattedString "%Y-%m-%d" "2021-01-01" :: Maybe Date -- Just 2021-01-01 -- -- >>> fromFormattedString "%Y%m%d" "20210101" :: Maybe Date -- Just 2021-01-01 -- -- >>> fromFormattedString "%Y%m%d" "202101" :: Maybe Date -- Nothing --fromFormattedString :: MonadFail m => String -> String -> m Date -- | Attempts to parse and return Date from a given Text with -- ISO format. -- --
-- >>> fromText "2021-01-01" :: Maybe Date -- Just 2021-01-01 -- -- >>> fromText "20210101" :: Maybe Date -- Nothing --fromText :: MonadFail m => Text -> m Date -- | Attempts to parse and return Date from a given Text with -- ISO format. -- --
-- >>> fromFormattedText "%Y-%m-%d" "2021-01-01" :: Maybe Date -- Just 2021-01-01 -- -- >>> fromFormattedText "%Y%m%d" "20210101" :: Maybe Date -- Just 2021-01-01 -- -- >>> fromFormattedText "%Y%m%d" "202101" :: Maybe Date -- Nothing --fromFormattedText :: MonadFail m => String -> Text -> m Date -- | Converts Date value to a Day value. -- --
-- >>> toDay (read "2021-01-01") -- 2021-01-01 --toDay :: Date -> Day -- | Converts Date value to a 3-tuple of year, month and day. -- --
-- >>> toYMD (read "2020-12-31") -- (2020,12,31) --toYMD :: Date -> (Integer, Int, Int) -- | Converts Date value into a String value with ISO format. -- --
-- >>> toString (read "2021-01-01") -- "2021-01-01" --toString :: Date -> String -- | Converts Date value into a String value with the given -- format. -- --
-- >>> toFormattedString "%Y-%m-%d" (read "2021-01-01") -- "2021-01-01" -- -- >>> toFormattedString "%d/%m/%Y" (read "2021-01-01") -- "01/01/2021" --toFormattedString :: String -> Date -> String -- | Converts Date value into a Text value with ISO format. -- --
-- >>> toText (read "2021-01-01") -- "2021-01-01" --toText :: Date -> Text -- | Converts Date value into a Text value with the given -- format. -- --
-- >>> toFormattedText "%Y-%m-%d" (read "2021-01-01") -- "2021-01-01" -- -- >>> toFormattedText "%d/%m/%Y" (read "2021-01-01") -- "01/01/2021" --toFormattedText :: String -> Date -> Text -- | Adds (or subtracts) some days. -- --
-- >>> addDays (-1) $ fromYMD 2021 1 1 -- 2020-12-31 -- -- >>> addDays 1 $ addDays (-1) $ fromYMD 2021 1 1 -- 2021-01-01 --addDays :: Integer -> Date -> Date instance GHC.Classes.Ord Haspara.Internal.Date.Date instance GHC.Enum.Enum Haspara.Internal.Date.Date instance GHC.Classes.Eq Haspara.Internal.Date.Date instance Data.Hashable.Class.Hashable Haspara.Internal.Date.Date instance GHC.Read.Read Haspara.Internal.Date.Date instance GHC.Show.Show Haspara.Internal.Date.Date instance Data.Aeson.Types.FromJSON.FromJSON Haspara.Internal.Date.Date instance Data.Aeson.Types.ToJSON.ToJSON Haspara.Internal.Date.Date -- | This module provides definitions and functions to encode and work on -- date values. module Haspara.Date -- | Type encoding for date values. -- -- This is a convenience wrapper around Day type. It helps us to -- avoid defining orphan instances. data Date -- | Builds a Date from a given Day. -- --
-- >>> fromDay (read "2021-01-01") -- 2021-01-01 --fromDay :: Day -> Date -- | Builds a Date from a given year, month and day as in Gregorian -- calendar. -- --
-- >>> fromYMD 2021 1 1 -- 2021-01-01 --fromYMD :: Integer -> Int -> Int -> Date -- | Attempts to parse and return Date from a given String -- with ISO format. -- --
-- >>> fromString "2021-01-01" :: Maybe Date -- Just 2021-01-01 -- -- >>> fromString "20210101" :: Maybe Date -- Nothing --fromString :: MonadFail m => String -> m Date -- | Attempts to parse and return Date from a given String -- with given date format. -- --
-- >>> fromFormattedString "%Y-%m-%d" "2021-01-01" :: Maybe Date -- Just 2021-01-01 -- -- >>> fromFormattedString "%Y%m%d" "20210101" :: Maybe Date -- Just 2021-01-01 -- -- >>> fromFormattedString "%Y%m%d" "202101" :: Maybe Date -- Nothing --fromFormattedString :: MonadFail m => String -> String -> m Date -- | Attempts to parse and return Date from a given Text with -- ISO format. -- --
-- >>> fromText "2021-01-01" :: Maybe Date -- Just 2021-01-01 -- -- >>> fromText "20210101" :: Maybe Date -- Nothing --fromText :: MonadFail m => Text -> m Date -- | Attempts to parse and return Date from a given Text with -- ISO format. -- --
-- >>> fromFormattedText "%Y-%m-%d" "2021-01-01" :: Maybe Date -- Just 2021-01-01 -- -- >>> fromFormattedText "%Y%m%d" "20210101" :: Maybe Date -- Just 2021-01-01 -- -- >>> fromFormattedText "%Y%m%d" "202101" :: Maybe Date -- Nothing --fromFormattedText :: MonadFail m => String -> Text -> m Date -- | Converts Date value to a Day value. -- --
-- >>> toDay (read "2021-01-01") -- 2021-01-01 --toDay :: Date -> Day -- | Converts Date value to a 3-tuple of year, month and day. -- --
-- >>> toYMD (read "2020-12-31") -- (2020,12,31) --toYMD :: Date -> (Integer, Int, Int) -- | Converts Date value into a String value with ISO format. -- --
-- >>> toString (read "2021-01-01") -- "2021-01-01" --toString :: Date -> String -- | Converts Date value into a String value with the given -- format. -- --
-- >>> toFormattedString "%Y-%m-%d" (read "2021-01-01") -- "2021-01-01" -- -- >>> toFormattedString "%d/%m/%Y" (read "2021-01-01") -- "01/01/2021" --toFormattedString :: String -> Date -> String -- | Converts Date value into a Text value with ISO format. -- --
-- >>> toText (read "2021-01-01") -- "2021-01-01" --toText :: Date -> Text -- | Converts Date value into a Text value with the given -- format. -- --
-- >>> toFormattedText "%Y-%m-%d" (read "2021-01-01") -- "2021-01-01" -- -- >>> toFormattedText "%d/%m/%Y" (read "2021-01-01") -- "01/01/2021" --toFormattedText :: String -> Date -> Text -- | Adds (or subtracts) some days. -- --
-- >>> addDays (-1) $ fromYMD 2021 1 1 -- 2020-12-31 -- -- >>> addDays 1 $ addDays (-1) $ fromYMD 2021 1 1 -- 2021-01-01 --addDays :: Integer -> Date -> Date module Haspara.Internal.Id -- | Type encoding for entity identifiers. -- -- This encoding allows us to provide a phantom type for distinguishing -- between identifiers of varying types and an underlying identifier -- type. -- -- For example: -- --
-- >>> data A = A -- -- >>> data B = B -- -- >>> data C = C -- -- >>> type IdA = Id A Int -- -- >>> type IdB = Id B Int -- -- >>> type IdC = Id C String -- -- >>> let idA = Id 1 :: IdA -- -- >>> let idB = Id 1 :: IdB -- -- >>> let idC = Id "C1" :: IdC -- -- >>> idA -- 1 -- -- >>> idB -- 1 -- -- >>> idC -- "C1" -- -- >>> idA == idA -- True -- -- >>> -- idA == idB -- Compile error as: Couldn't match type ‘B’ with ‘A’ ---- -- Hashes, on the otherhand, can be compared: -- --
-- >>> import Data.Hashable -- -- >>> hash idA == hash idB -- True --newtype Id a b Id :: b -> Id a b [unId] :: Id a b -> b -- | Type encoding for a lookup table from entity Ids to -- corresponding entities. -- --
-- >>> data A = A Int String deriving Show -- -- >>> type IdA = Id A Int -- -- >>> let a1 = A 1 "a1" -- -- >>> let a2 = A 2 "a2" -- -- >>> let a3 = A 3 "a3" -- -- >>> let table = HM.fromList [(Id 1, a1), (Id 2, a2), (Id 3, a3)] :: IdLookup A Int -- -- >>> HM.lookup (Id 1) table -- Just (A 1 "a1") --type IdLookup a b = HashMap (Id a b) a instance Data.Hashable.Class.Hashable b => Data.Hashable.Class.Hashable (Haspara.Internal.Id.Id a b) instance GHC.Classes.Ord b => GHC.Classes.Ord (Haspara.Internal.Id.Id a b) instance GHC.Classes.Eq b => GHC.Classes.Eq (Haspara.Internal.Id.Id a b) instance GHC.Show.Show b => GHC.Show.Show (Haspara.Internal.Id.Id a b) instance Data.Aeson.Types.FromJSON.FromJSON b => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Internal.Id.Id a b) instance Data.Aeson.Types.ToJSON.ToJSON b => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Internal.Id.Id a b) module Haspara.Id -- | Type encoding for entity identifiers. -- -- This encoding allows us to provide a phantom type for distinguishing -- between identifiers of varying types and an underlying identifier -- type. -- -- For example: -- --
-- >>> data A = A -- -- >>> data B = B -- -- >>> data C = C -- -- >>> type IdA = Id A Int -- -- >>> type IdB = Id B Int -- -- >>> type IdC = Id C String -- -- >>> let idA = Id 1 :: IdA -- -- >>> let idB = Id 1 :: IdB -- -- >>> let idC = Id "C1" :: IdC -- -- >>> idA -- 1 -- -- >>> idB -- 1 -- -- >>> idC -- "C1" -- -- >>> idA == idA -- True -- -- >>> -- idA == idB -- Compile error as: Couldn't match type ‘B’ with ‘A’ ---- -- Hashes, on the otherhand, can be compared: -- --
-- >>> import Data.Hashable -- -- >>> hash idA == hash idB -- True --newtype Id a b Id :: b -> Id a b [unId] :: Id a b -> b -- | Type encoding for a lookup table from entity Ids to -- corresponding entities. -- --
-- >>> data A = A Int String deriving Show -- -- >>> type IdA = Id A Int -- -- >>> let a1 = A 1 "a1" -- -- >>> let a2 = A 2 "a2" -- -- >>> let a3 = A 3 "a3" -- -- >>> let table = HM.fromList [(Id 1, a1), (Id 2, a2), (Id 3, a3)] :: IdLookup A Int -- -- >>> HM.lookup (Id 1) table -- Just (A 1 "a1") --type IdLookup a b = HashMap (Id a b) a module Haspara.Internal.Quantity -- | Type encoding for common quantity values with given scaling (digits -- after the decimal point). -- --
-- >>> 42 :: Quantity 0 -- 42 -- -- >>> 42 :: Quantity 1 -- 42.0 -- -- >>> 42 :: Quantity 2 -- 42.00 -- -- >>> 41 + 1 :: Quantity 2 -- 42.00 -- -- >>> 43 - 1 :: Quantity 2 -- 42.00 -- -- >>> 2 * 3 * 7 :: Quantity 2 -- 42.00 -- -- >>> negate (-42) :: Quantity 2 -- 42.00 -- -- >>> abs (-42) :: Quantity 2 -- 42.00 -- -- >>> signum (-42) :: Quantity 2 -- -1.00 -- -- >>> fromInteger 42 :: Quantity 2 -- 42.00 -- -- >>> quantity 0.415 :: Quantity 2 -- 0.42 -- -- >>> quantity 0.425 :: Quantity 2 -- 0.42 -- -- >>> quantityLossless 0.42 :: Either String (Quantity 2) -- Right 0.42 -- -- >>> quantityLossless 0.415 :: Either String (Quantity 2) -- Left "Underflow while trying to create quantity: 0.415" --newtype Quantity (s :: Nat) MkQuantity :: Decimal RoundHalfEven s Integer -> Quantity (s :: Nat) [unQuantity] :: Quantity (s :: Nat) -> Decimal RoundHalfEven s Integer -- | Constructs Quantity values from Scientific values in a -- lossy way. -- -- This function uses quantityAux in case that the lossless -- attempt fails. We could have used quantityAux directly. -- However, quantityAux is doing too much (see -- roundScientific). Therefore, we are first attempting a lossless -- construction (see quantityLossless) and we fallback to -- quantityAux in case the lossless construction fails. -- --
-- >>> quantity 0 :: Quantity 0 -- 0 -- -- >>> quantity 0 :: Quantity 1 -- 0.0 -- -- >>> quantity 0 :: Quantity 2 -- 0.00 -- -- >>> quantity 0.04 :: Quantity 1 -- 0.0 -- -- >>> quantity 0.05 :: Quantity 1 -- 0.0 -- -- >>> quantity 0.06 :: Quantity 1 -- 0.1 -- -- >>> quantity 0.14 :: Quantity 1 -- 0.1 -- -- >>> quantity 0.15 :: Quantity 1 -- 0.2 -- -- >>> quantity 0.16 :: Quantity 1 -- 0.2 -- -- >>> quantity 0.04 :: Quantity 2 -- 0.04 -- -- >>> quantity 0.05 :: Quantity 2 -- 0.05 -- -- >>> quantity 0.06 :: Quantity 2 -- 0.06 -- -- >>> quantity 0.14 :: Quantity 2 -- 0.14 -- -- >>> quantity 0.15 :: Quantity 2 -- 0.15 -- -- >>> quantity 0.16 :: Quantity 2 -- 0.16 -- -- >>> quantity 0.04 :: Quantity 3 -- 0.040 -- -- >>> quantity 0.05 :: Quantity 3 -- 0.050 -- -- >>> quantity 0.06 :: Quantity 3 -- 0.060 -- -- >>> quantity 0.14 :: Quantity 3 -- 0.140 -- -- >>> quantity 0.15 :: Quantity 3 -- 0.150 -- -- >>> quantity 0.16 :: Quantity 3 -- 0.160 --quantity :: KnownNat s => Scientific -> Quantity s -- | Auxiliary function for quantity implementation. -- -- See quantity why we need this function and why we haven't used -- it as the direct implementation of quantity. -- -- Call-sites should avoid using this function directly due to its -- performance characteristics. quantityAux :: forall s. KnownNat s => Scientific -> Quantity s -- | Constructs Quantity values from Scientific values in a -- lossy way. -- --
-- >>> quantityLossless 0 :: Either String (Quantity 0) -- Right 0 -- -- >>> quantityLossless 0 :: Either String (Quantity 1) -- Right 0.0 -- -- >>> quantityLossless 0 :: Either String (Quantity 2) -- Right 0.00 -- -- >>> quantityLossless 0.04 :: Either String (Quantity 1) -- Left "Underflow while trying to create quantity: 4.0e-2" -- -- >>> quantityLossless 0.05 :: Either String (Quantity 1) -- Left "Underflow while trying to create quantity: 5.0e-2" -- -- >>> quantityLossless 0.06 :: Either String (Quantity 1) -- Left "Underflow while trying to create quantity: 6.0e-2" -- -- >>> quantityLossless 0.14 :: Either String (Quantity 1) -- Left "Underflow while trying to create quantity: 0.14" -- -- >>> quantityLossless 0.15 :: Either String (Quantity 1) -- Left "Underflow while trying to create quantity: 0.15" -- -- >>> quantityLossless 0.16 :: Either String (Quantity 1) -- Left "Underflow while trying to create quantity: 0.16" -- -- >>> quantityLossless 0.04 :: Either String (Quantity 2) -- Right 0.04 -- -- >>> quantityLossless 0.05 :: Either String (Quantity 2) -- Right 0.05 -- -- >>> quantityLossless 0.06 :: Either String (Quantity 2) -- Right 0.06 -- -- >>> quantityLossless 0.14 :: Either String (Quantity 2) -- Right 0.14 -- -- >>> quantityLossless 0.15 :: Either String (Quantity 2) -- Right 0.15 -- -- >>> quantityLossless 0.16 :: Either String (Quantity 2) -- Right 0.16 -- -- >>> quantityLossless 0.04 :: Either String (Quantity 3) -- Right 0.040 -- -- >>> quantityLossless 0.05 :: Either String (Quantity 3) -- Right 0.050 -- -- >>> quantityLossless 0.06 :: Either String (Quantity 3) -- Right 0.060 -- -- >>> quantityLossless 0.14 :: Either String (Quantity 3) -- Right 0.140 -- -- >>> quantityLossless 0.15 :: Either String (Quantity 3) -- Right 0.150 -- -- >>> quantityLossless 0.16 :: Either String (Quantity 3) -- Right 0.160 --quantityLossless :: (KnownNat s, MonadError String m) => Scientific -> m (Quantity s) -- | Rounds given quantity by k digits. -- --
-- >>> roundQuantity (quantity 0.415 :: Quantity 3) :: Quantity 2 -- 0.42 -- -- >>> roundQuantity (quantity 0.425 :: Quantity 3) :: Quantity 2 -- 0.42 --roundQuantity :: KnownNat k => Quantity (n + k) -> Quantity n -- | Multiplies two quantities with different scales and rounds back to the -- scale of the frst operand. -- --
-- >>> times (quantity 0.42 :: Quantity 2) (quantity 0.42 :: Quantity 2) -- 0.18 --times :: (KnownNat s, KnownNat k) => Quantity s -> Quantity k -> Quantity s -- | Multiplies two quantities with different scales. -- --
-- >>> timesLossless (quantity 0.42 :: Quantity 2) (quantity 0.42 :: Quantity 2) -- 0.1764 --timesLossless :: (KnownNat s, KnownNat k) => Quantity s -> Quantity k -> Quantity (s + k) -- | Rounds a given scientific into a new scientific with given max digits -- after decimal point. -- -- This uses half-even rounding method. -- --
-- >>> roundScientific 0 0.4 -- 0.0 -- -- >>> roundScientific 0 0.5 -- 0.0 -- -- >>> roundScientific 0 0.6 -- 1.0 -- -- >>> roundScientific 0 1.4 -- 1.0 -- -- >>> roundScientific 0 1.5 -- 2.0 -- -- >>> roundScientific 0 1.6 -- 2.0 -- -- >>> roundScientific 1 0.04 -- 0.0 -- -- >>> roundScientific 1 0.05 -- 0.0 -- -- >>> roundScientific 1 0.06 -- 0.1 -- -- >>> roundScientific 1 0.14 -- 0.1 -- -- >>> roundScientific 1 0.15 -- 0.2 -- -- >>> roundScientific 1 0.16 -- 0.2 -- -- >>> roundScientific 1 3.650 -- 3.6 -- -- >>> roundScientific 1 3.740 -- 3.7 -- -- >>> roundScientific 1 3.749 -- 3.7 -- -- >>> roundScientific 1 3.750 -- 3.8 -- -- >>> roundScientific 1 3.751 -- 3.8 -- -- >>> roundScientific 1 3.760 -- 3.8 -- -- >>> roundScientific 1 (-3.650) -- -3.6 -- -- >>> roundScientific 1 (-3.740) -- -3.7 -- -- >>> roundScientific 1 (-3.749) -- -3.7 -- -- >>> roundScientific 1 (-3.750) -- -3.8 -- -- >>> roundScientific 1 (-3.751) -- -3.8 -- -- >>> roundScientific 1 (-3.760) -- -3.8 ---- -- TODO: Refactor to improve the performance of this function. roundScientific :: Int -> Scientific -> Scientific instance GHC.TypeNats.KnownNat s => GHC.Num.Num (Haspara.Internal.Quantity.Quantity s) instance GHC.Generics.Generic (Haspara.Internal.Quantity.Quantity s) instance GHC.Classes.Ord (Haspara.Internal.Quantity.Quantity s) instance GHC.Classes.Eq (Haspara.Internal.Quantity.Quantity s) instance Language.Haskell.TH.Syntax.Lift (Numeric.Decimal.Internal.Decimal Numeric.Decimal.RoundHalfEven s GHC.Integer.Type.Integer) instance Language.Haskell.TH.Syntax.Lift (Haspara.Internal.Quantity.Quantity s) instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Internal.Quantity.Quantity s) instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Internal.Quantity.Quantity s) instance GHC.TypeNats.KnownNat s => GHC.Num.Num (Numeric.Decimal.BoundedArithmetic.Arith (Haspara.Internal.Quantity.Quantity s)) instance GHC.TypeNats.KnownNat s => GHC.Real.Fractional (Numeric.Decimal.BoundedArithmetic.Arith (Haspara.Internal.Quantity.Quantity s)) instance GHC.TypeNats.KnownNat s => GHC.Show.Show (Haspara.Internal.Quantity.Quantity s) -- | This module provides internal definitions for modeling and working -- with FX rates. module Haspara.Internal.FXQuote -- | Type encoding for FX rates. data FXQuote (s :: Nat) MkFXQuote :: !Date -> !CurrencyPair -> !Refined Positive (Quantity s) -> FXQuote (s :: Nat) -- | Actual date of the FX rate. [fxQuoteDate] :: FXQuote (s :: Nat) -> !Date -- | Currency pair of the FX rate. [fxQuotePair] :: FXQuote (s :: Nat) -> !CurrencyPair -- | Rate value of the FX rate. [fxQuoteRate] :: FXQuote (s :: Nat) -> !Refined Positive (Quantity s) -- | Smart constructor for FXQuote values within MonadError -- context. fxquote :: (KnownNat s, MonadError String m) => Date -> Currency -> Currency -> Scientific -> m (FXQuote s) -- | Smart constructor for FXQuote values within MonadFail -- context. fxquoteFail :: (KnownNat s, MonadFail m) => Date -> Currency -> Currency -> Scientific -> m (FXQuote s) -- | Unsafe FXQuote constructor that errors if it fails. fxquoteUnsafe :: KnownNat s => Date -> Currency -> Currency -> Scientific -> FXQuote s instance GHC.Classes.Ord (Haspara.Internal.FXQuote.FXQuote s) instance GHC.Classes.Eq (Haspara.Internal.FXQuote.FXQuote s) instance GHC.TypeNats.KnownNat s => GHC.Show.Show (Haspara.Internal.FXQuote.FXQuote s) instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Internal.FXQuote.FXQuote s) instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Internal.FXQuote.FXQuote s) module Haspara.Internal.Money data Money (s :: Nat) MoneySome :: Date -> Currency -> Quantity s -> Money (s :: Nat) MoneyZero :: Money (s :: Nat) MoneyFail :: String -> Money (s :: Nat) mkMoney :: KnownNat s => Date -> Currency -> Quantity s -> Money s mkMoneyFromScientific :: KnownNat s => Date -> Currency -> Scientific -> Money s moneyDate :: KnownNat s => Money s -> Maybe Date moneyCurrency :: KnownNat s => Money s -> Maybe Currency moneyQuantity :: KnownNat s => Money s -> Maybe (Quantity s) -- | Converts the given Money value to another given currency with -- the given rate. -- --
-- >>> import Haspara -- -- >>> let eur = either error id $ currency "EUR" -- -- >>> let usd = either error id $ currency "USD" -- -- >>> let date = read "2021-01-01" :: Date -- -- >>> let eurmoney = mkMoney date eur (quantity 0.42 :: Quantity 2) :: Money 2 -- -- >>> convert eurmoney eur (quantity 1 :: Quantity 4) -- MoneySome 2021-01-01 EUR 0.42 -- -- >>> convert eurmoney usd (quantity 1 :: Quantity 4) -- MoneySome 2021-01-01 USD 0.42 -- -- >>> convert eurmoney usd (quantity 1.1516 :: Quantity 4) -- MoneySome 2021-01-01 USD 0.48 --convert :: (KnownNat s, KnownNat k) => Money s -> Currency -> Quantity k -> Money s -- | Converts the given Money value to another currency with the -- given FXQuote. convertWithQuote :: (KnownNat s, KnownNat k) => Money s -> FXQuote k -> Money s instance GHC.TypeNats.KnownNat s => GHC.Show.Show (Haspara.Internal.Money.Money s) instance GHC.Classes.Ord (Haspara.Internal.Money.Money s) instance GHC.Classes.Eq (Haspara.Internal.Money.Money s) instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.FromJSON.FromJSON (Haspara.Internal.Money.Money s) instance GHC.TypeNats.KnownNat s => Data.Aeson.Types.ToJSON.ToJSON (Haspara.Internal.Money.Money s) module Haspara.Internal.FXQuoteDatabase type FXQuoteDatabase (n :: Nat) = HashMap CurrencyPair (FXQuotePairDatabase n) data FXQuotePairDatabase (n :: Nat) FXQuotePairDatabase :: !CurrencyPair -> !HashMap Date (FXQuote n) -> !Date -> !Date -> FXQuotePairDatabase (n :: Nat) [fxQuotePairDatabasePair] :: FXQuotePairDatabase (n :: Nat) -> !CurrencyPair [fxQuotePairDatabaseTable] :: FXQuotePairDatabase (n :: Nat) -> !HashMap Date (FXQuote n) [fxQuotePairDatabaseSince] :: FXQuotePairDatabase (n :: Nat) -> !Date [fxQuotePairDatabaseUntil] :: FXQuotePairDatabase (n :: Nat) -> !Date findFXQuote :: KnownNat n => FXQuoteDatabase n -> CurrencyPair -> Date -> Maybe (FXQuote n) findFXQuoteAux :: KnownNat n => Date -> FXQuotePairDatabase n -> Maybe (FXQuote n) -- | This module provides base data definitions and functions for -- Haspara library. module Haspara.FXQuote -- | Type encoding for FX rates. data FXQuote (s :: Nat) -- | Actual date of the FX rate. fxQuoteDate :: FXQuote s -> Date -- | Currency pair of the FX rate. fxQuotePair :: FXQuote s -> CurrencyPair -- | Rate value of the FX rate. fxQuoteRate :: FXQuote s -> Refined Positive (Quantity s) -- | Smart constructor for FXQuote values within MonadError -- context. fxquote :: (KnownNat s, MonadError String m) => Date -> Currency -> Currency -> Scientific -> m (FXQuote s) -- | Smart constructor for FXQuote values within MonadFail -- context. fxquoteFail :: (KnownNat s, MonadFail m) => Date -> Currency -> Currency -> Scientific -> m (FXQuote s) type FXQuoteDatabase (n :: Nat) = HashMap CurrencyPair (FXQuotePairDatabase n) data FXQuotePairDatabase (n :: Nat) FXQuotePairDatabase :: !CurrencyPair -> !HashMap Date (FXQuote n) -> !Date -> !Date -> FXQuotePairDatabase (n :: Nat) [fxQuotePairDatabasePair] :: FXQuotePairDatabase (n :: Nat) -> !CurrencyPair [fxQuotePairDatabaseTable] :: FXQuotePairDatabase (n :: Nat) -> !HashMap Date (FXQuote n) [fxQuotePairDatabaseSince] :: FXQuotePairDatabase (n :: Nat) -> !Date [fxQuotePairDatabaseUntil] :: FXQuotePairDatabase (n :: Nat) -> !Date findFXQuote :: KnownNat n => FXQuoteDatabase n -> CurrencyPair -> Date -> Maybe (FXQuote n) -- | This module provides base data definitions and functions for -- Haspara library. module Haspara.Money data Money (s :: Nat) MoneySome :: Date -> Currency -> Quantity s -> Money (s :: Nat) MoneyZero :: Money (s :: Nat) MoneyFail :: String -> Money (s :: Nat) moneyDate :: KnownNat s => Money s -> Maybe Date moneyCurrency :: KnownNat s => Money s -> Maybe Currency moneyQuantity :: KnownNat s => Money s -> Maybe (Quantity s) mkMoney :: KnownNat s => Date -> Currency -> Quantity s -> Money s mkMoneyFromScientific :: KnownNat s => Date -> Currency -> Scientific -> Money s -- | Converts the given Money value to another given currency with -- the given rate. -- --
-- >>> import Haspara -- -- >>> let eur = either error id $ currency "EUR" -- -- >>> let usd = either error id $ currency "USD" -- -- >>> let date = read "2021-01-01" :: Date -- -- >>> let eurmoney = mkMoney date eur (quantity 0.42 :: Quantity 2) :: Money 2 -- -- >>> convert eurmoney eur (quantity 1 :: Quantity 4) -- MoneySome 2021-01-01 EUR 0.42 -- -- >>> convert eurmoney usd (quantity 1 :: Quantity 4) -- MoneySome 2021-01-01 USD 0.42 -- -- >>> convert eurmoney usd (quantity 1.1516 :: Quantity 4) -- MoneySome 2021-01-01 USD 0.48 --convert :: (KnownNat s, KnownNat k) => Money s -> Currency -> Quantity k -> Money s -- | Converts the given Money value to another currency with the -- given FXQuote. convertWithQuote :: (KnownNat s, KnownNat k) => Money s -> FXQuote k -> Money s -- | This module provides definitions and functions to encode and work on -- quantities with fixed decimal points. module Haspara.Quantity -- | Type encoding for common quantity values with given scaling (digits -- after the decimal point). -- --
-- >>> 42 :: Quantity 0 -- 42 -- -- >>> 42 :: Quantity 1 -- 42.0 -- -- >>> 42 :: Quantity 2 -- 42.00 -- -- >>> 41 + 1 :: Quantity 2 -- 42.00 -- -- >>> 43 - 1 :: Quantity 2 -- 42.00 -- -- >>> 2 * 3 * 7 :: Quantity 2 -- 42.00 -- -- >>> negate (-42) :: Quantity 2 -- 42.00 -- -- >>> abs (-42) :: Quantity 2 -- 42.00 -- -- >>> signum (-42) :: Quantity 2 -- -1.00 -- -- >>> fromInteger 42 :: Quantity 2 -- 42.00 -- -- >>> quantity 0.415 :: Quantity 2 -- 0.42 -- -- >>> quantity 0.425 :: Quantity 2 -- 0.42 -- -- >>> quantityLossless 0.42 :: Either String (Quantity 2) -- Right 0.42 -- -- >>> quantityLossless 0.415 :: Either String (Quantity 2) -- Left "Underflow while trying to create quantity: 0.415" --data Quantity (s :: Nat) unQuantity :: Quantity s -> Decimal RoundHalfEven s Integer -- | Constructs Quantity values from Scientific values in a -- lossy way. -- -- This function uses quantityAux in case that the lossless -- attempt fails. We could have used quantityAux directly. -- However, quantityAux is doing too much (see -- roundScientific). Therefore, we are first attempting a lossless -- construction (see quantityLossless) and we fallback to -- quantityAux in case the lossless construction fails. -- --
-- >>> quantity 0 :: Quantity 0 -- 0 -- -- >>> quantity 0 :: Quantity 1 -- 0.0 -- -- >>> quantity 0 :: Quantity 2 -- 0.00 -- -- >>> quantity 0.04 :: Quantity 1 -- 0.0 -- -- >>> quantity 0.05 :: Quantity 1 -- 0.0 -- -- >>> quantity 0.06 :: Quantity 1 -- 0.1 -- -- >>> quantity 0.14 :: Quantity 1 -- 0.1 -- -- >>> quantity 0.15 :: Quantity 1 -- 0.2 -- -- >>> quantity 0.16 :: Quantity 1 -- 0.2 -- -- >>> quantity 0.04 :: Quantity 2 -- 0.04 -- -- >>> quantity 0.05 :: Quantity 2 -- 0.05 -- -- >>> quantity 0.06 :: Quantity 2 -- 0.06 -- -- >>> quantity 0.14 :: Quantity 2 -- 0.14 -- -- >>> quantity 0.15 :: Quantity 2 -- 0.15 -- -- >>> quantity 0.16 :: Quantity 2 -- 0.16 -- -- >>> quantity 0.04 :: Quantity 3 -- 0.040 -- -- >>> quantity 0.05 :: Quantity 3 -- 0.050 -- -- >>> quantity 0.06 :: Quantity 3 -- 0.060 -- -- >>> quantity 0.14 :: Quantity 3 -- 0.140 -- -- >>> quantity 0.15 :: Quantity 3 -- 0.150 -- -- >>> quantity 0.16 :: Quantity 3 -- 0.160 --quantity :: KnownNat s => Scientific -> Quantity s -- | Constructs Quantity values from Scientific values in a -- lossy way. -- --
-- >>> quantityLossless 0 :: Either String (Quantity 0) -- Right 0 -- -- >>> quantityLossless 0 :: Either String (Quantity 1) -- Right 0.0 -- -- >>> quantityLossless 0 :: Either String (Quantity 2) -- Right 0.00 -- -- >>> quantityLossless 0.04 :: Either String (Quantity 1) -- Left "Underflow while trying to create quantity: 4.0e-2" -- -- >>> quantityLossless 0.05 :: Either String (Quantity 1) -- Left "Underflow while trying to create quantity: 5.0e-2" -- -- >>> quantityLossless 0.06 :: Either String (Quantity 1) -- Left "Underflow while trying to create quantity: 6.0e-2" -- -- >>> quantityLossless 0.14 :: Either String (Quantity 1) -- Left "Underflow while trying to create quantity: 0.14" -- -- >>> quantityLossless 0.15 :: Either String (Quantity 1) -- Left "Underflow while trying to create quantity: 0.15" -- -- >>> quantityLossless 0.16 :: Either String (Quantity 1) -- Left "Underflow while trying to create quantity: 0.16" -- -- >>> quantityLossless 0.04 :: Either String (Quantity 2) -- Right 0.04 -- -- >>> quantityLossless 0.05 :: Either String (Quantity 2) -- Right 0.05 -- -- >>> quantityLossless 0.06 :: Either String (Quantity 2) -- Right 0.06 -- -- >>> quantityLossless 0.14 :: Either String (Quantity 2) -- Right 0.14 -- -- >>> quantityLossless 0.15 :: Either String (Quantity 2) -- Right 0.15 -- -- >>> quantityLossless 0.16 :: Either String (Quantity 2) -- Right 0.16 -- -- >>> quantityLossless 0.04 :: Either String (Quantity 3) -- Right 0.040 -- -- >>> quantityLossless 0.05 :: Either String (Quantity 3) -- Right 0.050 -- -- >>> quantityLossless 0.06 :: Either String (Quantity 3) -- Right 0.060 -- -- >>> quantityLossless 0.14 :: Either String (Quantity 3) -- Right 0.140 -- -- >>> quantityLossless 0.15 :: Either String (Quantity 3) -- Right 0.150 -- -- >>> quantityLossless 0.16 :: Either String (Quantity 3) -- Right 0.160 --quantityLossless :: (KnownNat s, MonadError String m) => Scientific -> m (Quantity s) -- | Rounds given quantity by k digits. -- --
-- >>> roundQuantity (quantity 0.415 :: Quantity 3) :: Quantity 2 -- 0.42 -- -- >>> roundQuantity (quantity 0.425 :: Quantity 3) :: Quantity 2 -- 0.42 --roundQuantity :: KnownNat k => Quantity (n + k) -> Quantity n -- | Multiplies two quantities with different scales and rounds back to the -- scale of the frst operand. -- --
-- >>> times (quantity 0.42 :: Quantity 2) (quantity 0.42 :: Quantity 2) -- 0.18 --times :: (KnownNat s, KnownNat k) => Quantity s -> Quantity k -> Quantity s -- | Multiplies two quantities with different scales. -- --
-- >>> timesLossless (quantity 0.42 :: Quantity 2) (quantity 0.42 :: Quantity 2) -- 0.1764 --timesLossless :: (KnownNat s, KnownNat k) => Quantity s -> Quantity k -> Quantity (s + k) module Haspara -- | This module provides template-haskell functions for various -- Base definitions. module Haspara.TH -- | Constructs a Quantity value at compile-time using -- -XTemplateHaskell. -- --
-- >>> :set -XDataKinds -- -- >>> $$(quantityTH 0.00) :: Quantity 2 -- 0.00 -- -- >>> $$(quantityTH 0.09) :: Quantity 2 -- 0.09 -- -- >>> $$(quantityTH 0.009) :: Quantity 2 -- ... -- ..."Underflow while trying to create quantity: 9.0e-3" -- ... -- -- >>> $$(quantityTH 0.009) :: Quantity 3 -- 0.009 --quantityTH :: KnownNat s => Scientific -> Q (TExp (Quantity s)) -- | Constructs a Currency value at compile-time using -- -XTemplateHaskell. -- --
-- >>> $$(currencyTH "USD") -- USD -- -- >>> $$(currencyTH "usd") -- ... -- ...Currency code error! Expecting at least 3 uppercase characters, but received: "usd" -- ... --currencyTH :: Text -> Q (TExp Currency) -- | Constructs a CurrencyPair value at compile-time using -- -XTemplateHaskell. -- --
-- >>> $$(currencyPairTH "EUR" "USD") -- EUR/USD -- -- >>> $$(currencyPairTH "USD" "USD") -- ... -- ...Can not create currency pair from same currencies: USD and USD -- ... -- -- >>> $$(currencyPairTH "USD" "eur") -- ... -- ...Currency code error! Expecting at least 3 uppercase characters, but received: "eur" -- ... --currencyPairTH :: Text -> Text -> Q (TExp CurrencyPair)