References:
Q
, the quotation monad, seems to be the main thing.
Q Dec
)
General:
import Language.Haskell.TH.Syntax
Lift
typeclass
derive Lift
possible with DeriveLift
ghc extension$name
$$name
unType :: TExp a -> Exp a
: Remove type info[|| ‥ ||]
(E: Expresssion, P: Pattern, L: Literal?, T: type?)
ExpQ is an alias for Q Exp
Example use:
Clash.Prelude Language.Haskell.TH> runQ [| \x -> 1 |]
LamE [VarP x_0] (LitE (IntegerL 1))
Clash.Prelude Language.Haskell.TH> :t it
it :: Language.Haskell.TH.Exp
Prelude> :set -XTemplateHaskell
Prelude> import Language.Haskell.TH.Syntax
Prelude Language.Haskell.TH.Syntax>
Non-type values can be quoted with one single quote:
Prelude Language.Haskell.TH.Syntax> :t 'id
'id :: Name
Prelude Language.Haskell.TH.Syntax> 'id
GHC.Base.id
Prelude Language.Haskell.TH.Syntax> :t id
id :: a -> a
Type values can be quoted with two single quotes:
Prelude Language.Haskell.TH.Syntax> ''Bool
GHC.Types.Bool
Prelude Language.Haskell.TH.Syntax> :t ''Bool
''Bool :: Name
Prelude Language.Haskell.TH.Syntax> 'Bool
<interactive>:31:1: error:
• Not in scope: data constructor ‘Bool’
• In the Template Haskell quotation 'Bool
Prelude Language.Haskell.TH.Syntax> :t LamE
LamE :: [Pat] -> Exp -> Exp
Prelude Language.Haskell.TH.Syntax> :t VarP
VarP :: Name -> Pat
The TemplateHaskell extension got to enable for some stuff to work:
Prelude Language.Haskell.TH.Syntax> runQ [e| Just x |]
<interactive>:20:18: error: parse error on input ‘]’
Prelude Language.Haskell.TH.Syntax> :set -XTemplateHaskell
Prelude Language.Haskell.TH.Syntax> runQ [e| Just x |]
AppE (ConE GHC.Maybe.Just) (UnboundVarE x)
varname is used in a top-level splice, quasi-quote, or annotation,
and must be imported, not defined locally
—
From https://well-typed.com/blog/2020/06/th-for-static-data/:
However if we define isStaticIdSet and the Lift instance in the same module, GHC can’t call liftTyped as it’s not yet compiled by the time we need it.
Last letter of variable names usually follow this convention:
[| ... |]
brackets are known as 'Oxford brackets'. It reifies haskell (ie, concrete haskell syntax -> AST)$( ... )
does the de-reification. In TH parlance, it's called splicing.
From https://markkarpov.com/tutorial/th.html:
Declaration | [d | ‥ | ] | Q [Dec] |
Expression | [e | ‥ | ] | Q Exp |
Typed expression | [ | ‥ | ||
Type | [t | ‥ | ] | Q Type |
Pattern | [p | ‥ | ] | Q Pat |
https://hackage.haskell.org/package/template-haskell-2.23.0.0/docs/Language-Haskell-TH.html
> import Language.Haskell.TH
λ> a = mkName "hi"
λ> :t a
λa :: Name
ghci> :t reify ''Bool
reify ''Bool :: Q Info
-- https://github.com/seanwestfall/templatehaskell
ghci> $(stringE . show =<< reify ''Bool)
"TyConI
(DataD
[]
GHC.Types.Bool
[]
Nothing
[NormalC GHC.Types.False [],
NormalC GHC.Types.True []]
[])"
ghci> :t DataD [] (mkName "Foo") [] Nothing
DataD [] (mkName "Foo") [] Nothing :: [Con] -> [DerivClause] -> Dec
ghci> [d| data Foo = Foo Int |]
[DataD [] Foo_1 [] Nothing [NormalC Foo_2 [(Bang NoSourceUnpackedness NoSourceStrictness,ConT GHC.Types.Int)]] []]
ghci> [d| data Itype = C0 | C1 |]
[DataD [] Itype_3 [] Nothing [NormalC C0_4 [],NormalC C1_5 []] []]
ghci> [d| data Itype = C0 | C1 | C2 |]
[DataD [] Itype_6 [] Nothing [NormalC C0_7 [],NormalC C1_8 [],NormalC C2_9 []] []]
ghci> normalC (mkName "C0") []
NormalC C0 []
ghci> dataD (pure []) (mkName "Itype") [] Nothing [] []
DataD [] Itype [] Nothing [] []