haspara-0.0.0.5: A library providing definitions to work with monetary values.

Haspara.Quantity

Description

This module provides definitions for modeling and working with quantities with fixed decimal points.

Synopsis

# Data Definition

newtype Quantity (s :: Nat) Source #

Type encoding for quantity values with a 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
>>> mkQuantity 0.415 :: Quantity 2
0.42
>>> mkQuantity 0.425 :: Quantity 2
0.42
>>> mkQuantityLossless 0.42 :: Either String (Quantity 2)
Right 0.42
>>> mkQuantityLossless 0.415 :: Either String (Quantity 2)
Left "Underflow while trying to create quantity: 0.415"


Constructors

 MkQuantity FieldsunQuantity :: Decimal RoundHalfEven s Integer

#### Instances

Instances details
 Lift (Quantity s :: Type) Source # Lift instance for Quantity. Instance detailsDefined in Haspara.Quantity Methodslift :: Quantity s -> Q Exp #liftTyped :: Quantity s -> Q (TExp (Quantity s)) # Eq (Quantity s) Source # Instance detailsDefined in Haspara.Quantity Methods(==) :: Quantity s -> Quantity s -> Bool #(/=) :: Quantity s -> Quantity s -> Bool # KnownNat s => Fractional (Arith (Quantity s)) Source # Fractional arithmetic over Quantity values.>>> import Numeric.Decimal >>> arithM (fromRational 0.42) :: Either SomeException (Quantity 2) Right 0.42 >>> arithM (fromRational 0.415) :: Either SomeException (Quantity 2) Left PrecisionLoss (83 % 200) to 2 decimal spaces >>> arithM $(fromRational 0.84) / (fromRational 2) :: Either SomeException (Quantity 2) Right 0.42 >>> arithM$ (fromRational 0.42) / (fromRational 0) :: Either SomeException (Quantity 2) Left divide by zero >>> let a = 84 :: Quantity 2 >>> let b = 2 :: Quantity 2 >>> let c = 0 :: Quantity 2 >>> arithM (Arith a / Arith b) :: Either SomeException (Quantity 2) Right 42.00 >>> arithM (Arith a / Arith b / Arith c) :: Either SomeException (Quantity 2) Left divide by zero  Instance detailsDefined in Haspara.Quantity Methods(/) :: Arith (Quantity s) -> Arith (Quantity s) -> Arith (Quantity s) #recip :: Arith (Quantity s) -> Arith (Quantity s) # KnownNat s => Num (Arith (Quantity s)) Source # Numeric arithmetic over Quantity values.>>> import Numeric.Decimal >>> let a = Arith (mkQuantity 10) + Arith (mkQuantity 32) :: Arith (Quantity 2) >>> arithMaybe a Just 42.00 >>> arithM (41 + 1) :: Either SomeException (Quantity 2) Right 42.00 >>> arithM (43 - 1) :: Either SomeException (Quantity 2) Right 42.00 >>> arithM (2 * 3 * 7) :: Either SomeException (Quantity 2) Right 42.00 >>> arithM (signum 42) :: Either SomeException (Quantity 2) Right 1.00 >>> arithM (signum (-42)) :: Either SomeException (Quantity 2) Right -1.00 >>> arithM (abs 42) :: Either SomeException (Quantity 2) Right 42.00 >>> arithM (abs (-42)) :: Either SomeException (Quantity 2) Right 42.00 >>> arithM (fromInteger 42) :: Either SomeException (Quantity 2) Right 42.00  Instance detailsDefined in Haspara.Quantity Methods(+) :: Arith (Quantity s) -> Arith (Quantity s) -> Arith (Quantity s) #(-) :: Arith (Quantity s) -> Arith (Quantity s) -> Arith (Quantity s) #(*) :: Arith (Quantity s) -> Arith (Quantity s) -> Arith (Quantity s) #negate :: Arith (Quantity s) -> Arith (Quantity s) #abs :: Arith (Quantity s) -> Arith (Quantity s) #signum :: Arith (Quantity s) -> Arith (Quantity s) # KnownNat s => Num (Quantity s) Source # Instance detailsDefined in Haspara.Quantity Methods(+) :: Quantity s -> Quantity s -> Quantity s #(-) :: Quantity s -> Quantity s -> Quantity s #(*) :: Quantity s -> Quantity s -> Quantity s #negate :: Quantity s -> Quantity s #abs :: Quantity s -> Quantity s #signum :: Quantity s -> Quantity s # Ord (Quantity s) Source # Instance detailsDefined in Haspara.Quantity Methodscompare :: Quantity s -> Quantity s -> Ordering #(<) :: Quantity s -> Quantity s -> Bool #(<=) :: Quantity s -> Quantity s -> Bool #(>) :: Quantity s -> Quantity s -> Bool #(>=) :: Quantity s -> Quantity s -> Bool #max :: Quantity s -> Quantity s -> Quantity s #min :: Quantity s -> Quantity s -> Quantity s # KnownNat s => Show (Quantity s) Source # Show instance for Quantity.>>> show (42 :: Quantity 2) "42.00" >>> 42 :: Quantity 2 42.00  Instance detailsDefined in Haspara.Quantity MethodsshowsPrec :: Int -> Quantity s -> ShowS #show :: Quantity s -> String #showList :: [Quantity s] -> ShowS # Source # Instance detailsDefined in Haspara.Quantity Associated Typestype Rep (Quantity s) :: Type -> Type # Methodsfrom :: Quantity s -> Rep (Quantity s) x #to :: Rep (Quantity s) x -> Quantity s # KnownNat s => ToJSON (Quantity s) Source # ToJSON instance for Quantity.>>> Aeson.encode (mkQuantity 0.42 :: Quantity 2) "0.42"  Instance detailsDefined in Haspara.Quantity MethodstoJSON :: Quantity s -> Value #toJSONList :: [Quantity s] -> Value #toEncodingList :: [Quantity s] -> Encoding # KnownNat s => FromJSON (Quantity s) Source # FromJSON instance for Quantity.>>> Aeson.decode "0.42" :: Maybe (Quantity 2) Just 0.42 >>> Aeson.decode "0.415" :: Maybe (Quantity 2) Just 0.42 >>> Aeson.decode "0.425" :: Maybe (Quantity 2) Just 0.42  Instance detailsDefined in Haspara.Quantity Methods type Rep (Quantity s) Source # Instance detailsDefined in Haspara.Quantity type Rep (Quantity s) = D1 ('MetaData "Quantity" "Haspara.Quantity" "haspara-0.0.0.5-EkkfyzMRwPgIJNppmBSdYM" 'True) (C1 ('MetaCons "MkQuantity" 'PrefixI 'True) (S1 ('MetaSel ('Just "unQuantity") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Decimal RoundHalfEven s Integer))))

Type definition for unsigned Quantity values.

# Smart Constructors

Constructs Quantity values from Scientific values in a lossy way.

This function uses mkQuantityAux in case that the lossless attempt fails. We could have used mkQuantityAux directly. However, mkQuantityAux is doing too much (see roundScientific). Therefore, we are first attempting a lossless construction (see mkQuantityLossless) and we fallback to mkQuantityAux in case the lossless construction fails.

>>> mkQuantity 0 :: Quantity 0
0
>>> mkQuantity 0 :: Quantity 1
0.0
>>> mkQuantity 0 :: Quantity 2
0.00
>>> mkQuantity 0.04 :: Quantity 1
0.0
>>> mkQuantity 0.05 :: Quantity 1
0.0
>>> mkQuantity 0.06 :: Quantity 1
0.1
>>> mkQuantity 0.14 :: Quantity 1
0.1
>>> mkQuantity 0.15 :: Quantity 1
0.2
>>> mkQuantity 0.16 :: Quantity 1
0.2
>>> mkQuantity 0.04 :: Quantity 2
0.04
>>> mkQuantity 0.05 :: Quantity 2
0.05
>>> mkQuantity 0.06 :: Quantity 2
0.06
>>> mkQuantity 0.14 :: Quantity 2
0.14
>>> mkQuantity 0.15 :: Quantity 2
0.15
>>> mkQuantity 0.16 :: Quantity 2
0.16
>>> mkQuantity 0.04 :: Quantity 3
0.040
>>> mkQuantity 0.05 :: Quantity 3
0.050
>>> mkQuantity 0.06 :: Quantity 3
0.060
>>> mkQuantity 0.14 :: Quantity 3
0.140
>>> mkQuantity 0.15 :: Quantity 3
0.150
>>> mkQuantity 0.16 :: Quantity 3
0.160


mkQuantityLossless :: (KnownNat s, MonadError String m) => Scientific -> m (Quantity s) Source #

Constructs Quantity values from Scientific values in a lossy way.

>>> mkQuantityLossless 0 :: Either String (Quantity 0)
Right 0
>>> mkQuantityLossless 0 :: Either String (Quantity 1)
Right 0.0
>>> mkQuantityLossless 0 :: Either String (Quantity 2)
Right 0.00
>>> mkQuantityLossless 0.04 :: Either String (Quantity 1)
Left "Underflow while trying to create quantity: 4.0e-2"
>>> mkQuantityLossless 0.05 :: Either String (Quantity 1)
Left "Underflow while trying to create quantity: 5.0e-2"
>>> mkQuantityLossless 0.06 :: Either String (Quantity 1)
Left "Underflow while trying to create quantity: 6.0e-2"
>>> mkQuantityLossless 0.14 :: Either String (Quantity 1)
Left "Underflow while trying to create quantity: 0.14"
>>> mkQuantityLossless 0.15 :: Either String (Quantity 1)
Left "Underflow while trying to create quantity: 0.15"
>>> mkQuantityLossless 0.16 :: Either String (Quantity 1)
Left "Underflow while trying to create quantity: 0.16"
>>> mkQuantityLossless 0.04 :: Either String (Quantity 2)
Right 0.04
>>> mkQuantityLossless 0.05 :: Either String (Quantity 2)
Right 0.05
>>> mkQuantityLossless 0.06 :: Either String (Quantity 2)
Right 0.06
>>> mkQuantityLossless 0.14 :: Either String (Quantity 2)
Right 0.14
>>> mkQuantityLossless 0.15 :: Either String (Quantity 2)
Right 0.15
>>> mkQuantityLossless 0.16 :: Either String (Quantity 2)
Right 0.16
>>> mkQuantityLossless 0.04 :: Either String (Quantity 3)
Right 0.040
>>> mkQuantityLossless 0.05 :: Either String (Quantity 3)
Right 0.050
>>> mkQuantityLossless 0.06 :: Either String (Quantity 3)
Right 0.060
>>> mkQuantityLossless 0.14 :: Either String (Quantity 3)
Right 0.140
>>> mkQuantityLossless 0.15 :: Either String (Quantity 3)
Right 0.150
>>> mkQuantityLossless 0.16 :: Either String (Quantity 3)
Right 0.160


# Utilities

roundQuantity :: KnownNat k => Quantity (n + k) -> Quantity n Source #

Rounds given quantity by k digits.

>>> roundQuantity (mkQuantity 0.415 :: Quantity 3) :: Quantity 2
0.42
>>> roundQuantity (mkQuantity 0.425 :: Quantity 3) :: Quantity 2
0.42


times :: (KnownNat s, KnownNat k) => Quantity s -> Quantity k -> Quantity s Source #

Multiplies two quantities with different scales and rounds back to the scale of the frst operand.

>>> times (mkQuantity 0.42 :: Quantity 2) (mkQuantity 0.42 :: Quantity 2)
0.18


timesLossless :: (KnownNat s, KnownNat k) => Quantity s -> Quantity k -> Quantity (s + k) Source #

Multiplies two quantities with different scales.

>>> timesLossless (mkQuantity 0.42 :: Quantity 2) (mkQuantity 0.42 :: Quantity 2)
0.1764


Returns the total of a list of unsigned quantities.

>>> sumUnsignedQuantity [] :: UnsignedQuantity 2
Refined 0.00


Returns the absolute value of the Quantity as UnsignedQuantity.

>>> abs (mkQuantity 0.42 :: Quantity 2)
0.42
>>> abs (mkQuantity 0 :: Quantity 2)
0.00
>>> abs (mkQuantity (-0.42) :: Quantity 2)
0.42


# Internal

mkQuantityAux :: forall s. KnownNat s => Scientific -> Quantity s Source #

Auxiliary function for constructing Quantity values.

See mkQuantity why we need this function and why we haven't used it as the direct implementation of mkQuantity.

Call-sites should avoid using this function directly due to its performance characteristics.

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.

# Orphan instances

 Source # Orphan Lift instance for Quantity.TODO: Avoid having an orphan instance for Decimal r s p? Instance details Methods