| Copyright | © 2018 Phil de Joux © 2018 Block Scope Limited |
|---|---|
| License | MPL-2.0 |
| Maintainer | Phil de Joux <phil.dejoux@blockscope.com> |
| Stability | experimental |
| Safe Haskell | None |
| Language | Haskell2010 |
Data.Via.Scientific
Description
For encoding and decoding newtype rationals as scientific with a fixed number of decimal places.
- newtype DecimalPlaces = DecimalPlaces Int
- data ViaSci n where
- ViaSci :: (DefaultDecimalPlaces n, Newtype n Rational) => n -> ViaSci n
- class DefaultDecimalPlaces a where
- dpDegree :: DecimalPlaces
- showSci :: DecimalPlaces -> Scientific -> String
- fromSci :: Scientific -> Rational
- toSci :: DecimalPlaces -> Rational -> Scientific
- deriveDecimalPlaces :: DecimalPlaces -> Name -> Q [Dec]
- deriveJsonViaSci :: Name -> Q [Dec]
- deriveCsvViaSci :: Name -> Q [Dec]
Usage
Let's say we have a latitude that is a newtype Rational number but we
want it to be encoded to JSON with a fixed number of decimal places.
>>>newtype Lat = Lat Rational deriving (Eq, Ord, Show)
Types going ViaSci also need to be instances of DefaultDecimalPlaces and
Newtype.
>>>:{instance DefaultDecimalPlaces Lat where defdp _ = DecimalPlaces 8 instance Newtype Lat Rational where pack = Lat unpack (Lat a) = a instance ToJSON Lat where toJSON x = toJSON $ ViaSci x instance FromJSON Lat where parseJSON o = do ViaSci x <- parseJSON o; return x :}
>>>let x = 1122334455667788 % 10000000000000000>>>fromRational x0.1122334455667788>>>toSci (DecimalPlaces 8) x0.11223345
When having to check numbers by hand, a fixed decimal is more familiar than a ratio of possibly large integers.
>>>encode x"{\"numerator\":280583613916947,\"denominator\":2500000000000000}">>>encode (Lat x)"0.11223345"
With too few decimal places, the encoding will be lossy.
>>>decode (encode x) == Just xTrue>>>decode (encode (Lat x)) == Just (Lat x)False>>>let Just (Lat y) = decode (encode (Lat x)) in fromRational y0.11223345
Similarly for CSV.
>>>:{instance ToField Lat where toField = toField . ViaSci instance FromField Lat where parseField c = do ViaSci x <- parseField c; return x :}
>>>Csv.encode [("A", Lat x)]"A,0.11223345\r\n">>>Csv.decode Csv.NoHeader (Csv.encode [("B", Lat x)]) == Right (fromList [("B", Lat x)])False>>>Csv.decode Csv.NoHeader (Csv.encode [("C", Lat x)]) == Right (fromList [("C", Lat . fromSci . toSci (DecimalPlaces 8) $ x)])True
Decimal Places
newtype DecimalPlaces Source #
A positive number of decimal places.
Constructors
| DecimalPlaces Int |
Instances
An intermediate type used during encoding to JSON with aeson and during
encoding to CSV with cassava. It's also used during decoding.
The original type, a newtype Rational, goes to and fro via
scientific so that the rational value can be encoded as a scientific
value with a fixed number of decimal places.
Constructors
| ViaSci :: (DefaultDecimalPlaces n, Newtype n Rational) => n -> ViaSci n |
Instances
| Eq n => Eq (ViaSci n) Source # | |
| Ord n => Ord (ViaSci n) Source # | |
| Show n => Show (ViaSci n) Source # | |
| (DefaultDecimalPlaces n, Newtype n Rational) => ToJSON (ViaSci n) Source # | |
| (DefaultDecimalPlaces n, Newtype n Rational) => FromJSON (ViaSci n) Source # | |
| (DefaultDecimalPlaces n, Newtype n Rational) => FromField (ViaSci n) Source # | |
| (DefaultDecimalPlaces n, Newtype n Rational) => ToField (ViaSci n) Source # | |
class DefaultDecimalPlaces a where Source #
A default number of decimal places for a type.
Methods
defdp :: a -> DecimalPlaces Source #
dpDegree :: DecimalPlaces Source #
A choice of 8 decimal places for decimal degrees is just a bit more than a mm at the equator and less elsewhere.
- 1.1132 mm at the equator
- 1.0247 mm at 23° N/S
- 787.1 µm at 45° N/S
- 434.96 µm at 67° N/S
showSci :: DecimalPlaces -> Scientific -> String Source #
Shows a Scientific value with a fixed number of decimal places.
>>>let x = 0.1122334455667788>>>showSci (DecimalPlaces 16) x"0.1122334455667788">>>showSci (DecimalPlaces 8) x"0.11223345">>>showSci (DecimalPlaces 4) x"0.1122">>>showSci (DecimalPlaces 1) x"0.1">>>showSci (DecimalPlaces 0) x"0">>>showSci (DecimalPlaces (-1)) x"0">>>showSci (DecimalPlaces 32) x"0.11223344556677880000000000000000"
Conversions
fromSci :: Scientific -> Rational Source #
From Scientific exactly to Rational.
>>>let x = 0.1122334455667788>>>fromSci x4043636029064415 % 36028797018963968>>>x == fromRational (fromSci x)True
toSci :: DecimalPlaces -> Rational -> Scientific Source #
To Scientific from Rational as near as possible up to the given number
of DecimalPlaces with rounding.
>>>let x = 1122334455667788 % 10000000000000000>>>toSci (DecimalPlaces 8) x0.11223345>>>x == toRational (toSci (DecimalPlaces 8) x)False>>>x == toRational (toSci (DecimalPlaces 16) x)True>>>x == toRational (toSci (DecimalPlaces 32) x)True
Deriving instances with Template Haskell
deriveDecimalPlaces :: DecimalPlaces -> Name -> Q [Dec] Source #
Taking a number of decimal places from the given DecimalPlaces newtype,
derives an instance of DefaultDecimalPlaces.
>>>deriveDecimalPlaces (DecimalPlaces 8) ''Lat...
deriveCsvViaSci :: Name -> Q [Dec] Source #
Similar to deriveJsonViaSci but for instances of ToField and FromField.
>>>deriveCsvViaSci ''Lat...