haspara-0.0.0.0: src/Haspara/TH.hs
-- | This module provides template-haskell functions for various 'Haspara.Core.Base'
-- definitions.
--
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module Haspara.TH where
import Control.Monad (join)
import Data.Function (fix)
import Data.Scientific (Scientific)
import qualified Data.Text as T
import GHC.TypeLits (KnownNat)
import Haspara.Currency (Currency, CurrencyPair, currency, currencyPair)
import Haspara.Quantity (Quantity, quantityLossless)
import qualified Language.Haskell.TH.Syntax as 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 -> TH.Q (TH.TExp (Quantity s))
quantityTH = fix $ \loop -> fmap TH.TExp . either (fail . show) TH.lift . quantityWE (loop undefined)
where
-- This provides a work-around for the type-inference due the `s` type parameter.
-- Trick is borrowed from the Haskell `refined` library.
quantityWE :: KnownNat s => TH.Q (TH.TExp (Quantity s)) -> Scientific -> Either String (Quantity s)
quantityWE = const quantityLossless
-- | 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 :: T.Text -> TH.Q (TH.TExp Currency)
currencyTH = either fail (fmap TH.TExp . TH.lift) . 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 :: T.Text -> T.Text -> TH.Q (TH.TExp CurrencyPair)
currencyPairTH = (either fail (fmap TH.TExp . TH.lift) .) . mkPair
where
mkPair :: T.Text -> T.Text -> Either String CurrencyPair
mkPair x y = join $ currencyPair <$> currency x <*> currency y
-- -- | Constructs an 'FXQuote' value at compile-time using @-XTemplateHaskell@.
-- --
-- -- >>> :set -XDataKinds
-- -- >>> $$(fxquoteTH (read "2021-01-01") "EUR" "USD" 10) :: FXQuote 2
-- -- ("EUR/USD","2021-01-01","10.00")
-- fxquoteTH :: KnownNat s => Date -> T.Text -> T.Text -> Scientific -> TH.Q (TH.TExp (FXQuote s))
-- fxquoteTH d c1 c2 = fix $ \loop -> fmap TH.TExp . either (fail . show) TH.lift . fxquoteWE (loop undefined)
-- where
-- -- This provides a work-around for the type-inference due the `s` type parameter.
-- -- Trick is borrowed from the Haskell `refined` library.
-- fxquoteWE :: KnownNat s => TH.Q (TH.TExp (FXQuote s)) -> Scientific -> Either String (FXQuote s)
-- fxquoteWE _ v = do
-- xc1 <- currency c1
-- xc2 <- currency c2
-- either (Left . show) Right $ fxquote d xc1 xc2 v