{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# HLINT ignore "Avoid lambda using `infix`" #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
module Try.TemplateHaskell.ConstructorTags.Declare where
import Control.Lens (Iso', iso, (&), (<>~))
import Control.Monad
import Language.Haskell.TH
data HydraEvent
= GetUTxOResponse Int
| TxValid Int
p1 :: Q [Dec]
p1 =
[d|
data HydraEvent
= GetUTxOResponse Int
| TxValid Int
|]
-- >>> runQ p1
-- [DataD [] HydraEvent_159 [] Nothing [NormalC GetUTxOResponse_160 [(Bang NoSourceUnpackedness NoSourceStrictness,ConT GHC.Types.Int)],NormalC TxValid_161 [(Bang NoSourceUnpackedness NoSourceStrictness,ConT GHC.Types.Int)]] []]
data HydraEventKind
= GetUTxOResponseKind
| TxValidKind
deriving stock (Eq, Show)
p2 :: Q [Dec]
p2 =
[d|
data HydraEventKind
= GetUTxOResponseKind
| TxValidKind
deriving stock (Eq, Show)
|]
-- >>> runQ p2
-- [DataD [] HydraEventKind_156 [] Nothing [NormalC GetUTxOResponseKind_157 [],NormalC TxValidKind_158 []] [DerivClause (Just StockStrategy) [ConT GHC.Classes.Eq,ConT GHC.Show.Show]]]
nm :: Iso' Name String
nm = iso from to
where
from = nameBase
to = mkName
deriveTags :: Name -> String -> [Name] -> Q [Dec]
deriveTags ty suff classes = do
(TyConI tyCon) <- reify ty
(tyName, cs) <- case tyCon of
DataD _ n _ _ cs _ -> pure (n, cs)
NewtypeD _ n _ _ cs _ -> pure (n, [cs])
_ -> fail "deriveTags: only 'data' and 'newtype' are supported"
cs' <-
forM
cs
( let mk n = pure $ NormalC (n & nm <>~ suff) []
in \case
NormalC n _ -> mk n
RecC n _ -> mk n
_ -> fail "deriveTags: constructor names must be NormalC or RecC (See https://hackage.haskell.org/package/template-haskell-2.20.0.0/docs/src/Language.Haskell.TH.Syntax.html#Con)"
)
let v = DataD [] (tyName & nm <>~ suff) [] Nothing cs' [DerivClause (Just StockStrategy) (ConT <$> classes)]
pure [v]
getHydraEventKind :: HydraEvent -> HydraEventKind
getHydraEventKind event = case event of
GetUTxOResponse{} -> GetUTxOResponseKind
TxValid{} -> TxValidKind
p3 :: Q [Dec]
p3 =
[d|
getHydraEventKind :: HydraEvent -> HydraEventKind
getHydraEventKind event = case event of
GetUTxOResponse{} -> GetUTxOResponseKind
TxValid{} -> TxValidKind
|]
-- >>> runQ p3
-- [SigD getHydraEventKind_168 (AppT (AppT ArrowT (ConT Try.TemplateHaskell.Declare.HydraEvent)) (ConT Try.TemplateHaskell.Declare.HydraEventKind)),FunD getHydraEventKind_168 [Clause [VarP event_169] (NormalB (CaseE (VarE event_169) [Match (RecP Try.TemplateHaskell.Declare.GetUTxOResponse []) (NormalB (ConE Try.TemplateHaskell.Declare.GetUTxOResponseKind)) [],Match (RecP Try.TemplateHaskell.Declare.TxValid []) (NormalB (ConE Try.TemplateHaskell.Declare.TxValidKind)) []])) []]]
deriveMapping :: Name -> String -> String -> Q [Dec]
deriveMapping ty suff mappingName = do
(TyConI tyCon) <- reify ty
(tyName, cs) <- case tyCon of
DataD _ n _ _ cs _ -> pure (n, cs)
NewtypeD _ n _ _ cs _ -> pure (n, [cs])
_ -> fail "deriveTags: only 'data' and 'newtype' are supported"
let
sig = SigD (mkName mappingName) (AppT (AppT ArrowT (ConT ty)) (ConT (tyName & nm <>~ suff)))
event <- newName "event"
matches <-
forM
cs
( let mk n = pure $ Match (RecP n []) (NormalB (ConE (n & nm <>~ suff))) []
in \case
NormalC n _ -> mk n
RecC n _ -> mk n
_ -> fail "deriveTags: constructor names must be NormalC or RecC (See https://hackage.haskell.org/package/template-haskell-2.20.0.0/docs/src/Language.Haskell.TH.Syntax.html#Con)"
)
let
fun = FunD (mkName mappingName) [Clause [VarP event] (NormalB (CaseE (VarE event) matches)) []]
pure [sig, fun]