registry-0.1.9.1: test/Test/Data/Registry/THSpec.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}
module Test.Data.Registry.THSpec where
import Data.Generics.Product.Typed
import Data.Registry
import Data.Registry.TH
import Protolude
import Universum ((...))
-- * Example of TemplateHaskell usage to create the typeclass corresponding to a component
-- | We define 2 low-level components: Logger and Tracer
-- We make the corresponding typeclasses
data Logger m = Logger {
-- a function taking 2 arguments
_info :: Text -> Text -> m ()
-- a function having a constraint
, _error :: forall a . (Monoid a) => a -> Text -> m ()
}
makeTypeclass ''Logger
newtype Tracer m = Tracer {
_traceIt :: Text -> m ()
}
makeTypeclass ''Tracer
-- | Now we make a component which is going to use both components
newtype Service m = Service {
doIt :: Int -> Text -> m ()
}
newService :: Monad m => Logger m -> Tracer m -> Service m
newService logger tracer = Service {
doIt = run ... implementService
} where
-- the typeclass instances can be resolved by using a Reader containing
-- a tuple with all the components
run = flip runReaderT (logger, tracer)
-- | Implement the service using typeclasses
implementService ::
Monad m
=> WithLogger m
=> WithTracer m
=> Int
-> Text
-> m ()
implementService n t = do
info "doing it" t
traceIt (show n)
-- * Example of TemplateHaskell usage to check the completeness of a registry
add0 :: Int -> Text
add0 _ = ""
times2 :: Double -> Text
times2 n = show (n * 2)
reg0 :: Registry '[Int] '[Text, Int, Int]
reg0 = fun add0 +: val (1::Int) +: val (2 :: Int) +: end
-- See the gory details of why this is necessary: https://gitlab.haskell.org/ghc/ghc/issues/9813
$(return [])
-- | this compiles ok and de-duplicates types
reg1 :: Registry '[Int] '[Text, Int]
reg1 = $(checkRegistry 'reg0)
-- Then we can use makeFast
value = makeFast @Int reg1
regIncomplete :: Registry [Double, Int] [Text, Text, Int, Int]
regIncomplete = fun times2 +: reg0
-- | This does not compile
-- reg2 :: Registry [Double, Int] [Text, Int]
-- reg2 = $(checkRegistry 'regIncomplete)
-- | Using the checkRegistry function with IO functions
addIO0 :: Int -> IO Text
addIO0 _ = pure ""
regIO0 :: Registry '[IO Int] '[IO Text, IO Int, IO Int]
regIO0 = funTo @IO addIO0 +: valTo @IO (1 :: Int) +: valTo @IO (2 :: Int) +: end
-- See the gory details of why this is necessary: https://gitlab.haskell.org/ghc/ghc/issues/9813
$(return [])
regIO1 :: Registry '[IO Int] '[IO Text, IO Int]
regIO1 = $(checkRegistry 'regIO0)
-- Then we can use makeFast
valueIO = makeFast @(IO Int) regIO1