packages feed

registry-0.1.9.3: test/Test/Data/Registry/SimpleExamples.hs

{-# LANGUAGE DataKinds        #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}

{-
  These examples are simply provided to show what must compile
-}
module Test.Data.Registry.SimpleExamples where

import           Data.Registry
import           Data.Text     as T (length)
import           Protolude hiding (C1)


-- | No typeclass instance is necessary for a "record of functions" to be a Registry component
data Logging = Logging {
  info  :: Text -> IO ()
, debug :: Text -> IO ()
}

logging = make @Logging (fun Logging { info = print, debug = print } +: end)

-- | Simple datatypes which can be used in a registry
newtype Text1 = Text1 Text deriving (Eq, Show)
newtype Text2 = Text2 Text deriving (Eq, Show)
newtype Int1 = Int1 Int deriving (Eq, Show)

-- | values and functions
int1 :: Int
int1 = 1

add1 :: Int -> Text
add1 i = show (i + 1)

add2 :: Int -> Text -> Text1
add2 i j = Text1 (show (i+1) <> j)

text1 :: Text
text1 = "text1"

toText2 :: Text1 -> Text2
toText2 (Text1 t) = Text2 t

registry1 :: Registry [Int, Text, Text1] [Int, Text, Text1, Text2]
registry1 = normalize $
     val int1
  <: fun add1
  <: fun add2
  <: fun toText2

countSize :: Text -> Maybe Int
countSize t = Just (T.length t)

m = make @Text $ fun (\(t::Text) -> t) +: end

made1 :: Text
made1 = make @Text registry1

made2 :: Text1
made2 = make @Text1 registry1

made3 :: Text2
made3 = make @Text2 registry1

--
countSize1 :: Text -> Int1
countSize1 t = Int1 (T.length t)

registry2 :: Registry '[Int, Text] '[Int, Text, Int1]
registry2 =
     fun int1
  <: fun add1
  <: fun countSize1

made4 :: Int1
made4 = make @Int1 registry2

-- | This does *not* compile because Double in not in the
--   list of outputs for registry2
{-
wrong :: Double
wrong = make @Double registry2
-}

-- | This does *not* compile because the list of inputs
--   in registry2 is not included in the list of outputs
unknown :: Double -> Text1
unknown _ = Text1 "text1"

registry3 :: Registry [Double, Int, Text] [Int, Text1, Text, Int1]
registry3 =
     val int1
  <: fun unknown
  <: fun add1
  <: fun countSize1

-- | This does not compile because we need a double
--   to make Text1 and it is not in the list of outputs
{-
wrong :: Text1
wrong = make @Text1 registry3
-}

-- | This version compiles but throws an exception at runtime
dangerous :: Text1
dangerous = makeUnsafe @Text1 registry3

-- | Example with an optional configuration
--   The ProductionComponent will only be used if the ServiceConfig says we are in production
newtype ProductionComponent = ProductionComponent { doItInProduction :: IO () }
data ProductionConfig = ProductionConfig deriving (Eq, Show)

newtype Service = Service { service :: IO () }
data ServiceConfig = InProd | InDev deriving (Eq, Show)

-- | Production component definition

-- Normal constructor
newProductionComponent :: ProductionConfig -> Logging -> ProductionComponent
newProductionComponent _ _ = ProductionComponent (pure ())

-- Constructor which returns a "builder function", only used if a `ProductionConfig` is available
newProductionComponentBuilder :: Logging -> Tag "builder" (ProductionConfig -> ProductionComponent)
newProductionComponentBuilder = Tag . flip newProductionComponent

-- | Service component definition
--   It uses a builder for the ProductionComponent and only uses it in production
newService :: ServiceConfig -> Tag "builder" (ProductionConfig -> ProductionComponent) -> Service
newService InDev _ = Service { service = print ("dev" :: Text) }
newService InProd f = Service { service = doItInProduction ((unTag f) ProductionConfig)  }

registryWithOptionalComponents =
     val InDev
  <: fun newService
  <: fun newProductionComponentBuilder
  <: fun logging

makeService = make @Service registryWithOptionalComponents