Q
, for this there is a set of auxiliary functions that “raise” (wrap in Q
) the constructors of the Exp
, Lit
, Pat
types: lamE
(respectively LamE
), varE
, appE
, varP
, etc. d. Their signatures also use renumbered raised types: ExpQ = Q Exp
, LitQ = Q Lit
, PatQ = Q Pat
... (all of them can be found in the Language.Haskell.TH.Lib
module). Using these functions, you can significantly reduce the code, less often using do-syntax.lift
function, which raises to Q Exp
value of any type from the Lift
class.mkName
∷ String → Name
. There is also an auxiliary function dyn
s = return (VarE (mkName s))
, which returns the Exp
value representing the variable with the given name ( dyn ∷ String → Q Exp
).Exp
values that represent an abstract syntax tree is a time consuming and boring job. But fortunately, Template Haskell has quoting brackets that translate a specific Haskell code into a structure that represents it. [e| … |]
[e| … |]
or [| … |]
[| … |]
for expressions ( ∷ Q Exp
)[d| … |]
[d| … |]
for announcements ( ∷ Q [Dec]
)[t| … |]
[t| … |]
for types ( ∷ Q Type
)[p| … |]
[p| … |]
for samples (patterns) ( ∷ Q Pat
)[| λ _ → 0 |]
[| λ _ → 0 |]
is a structure (return $ LamE [WildP] (LitE (IntegerL 0)))
. The quotation is of type Q Exp
(and not just Exp
), so it must be computed inside the citation monad, which allows Template Haskell to replace all identifiers appearing within the quotation with unique ones generated with newName
. For example, quote [| λx → x |]
[| λx → x |]
will be converted to this code: do id ← newName "x" return $ LamE [VarP id] (VarE id)
[| 1 + $(fx) |]
[| 1 + $(fx) |]
calculate (fx)
, which must be of type Q Exp
, the result (structure of type Exp
), the result will be presented in the form of a usual Haskell-code and will insert ( paste ) it in place of $(fx)
, and then continue quoting — converting the resulting code into a structure that represents it. Thanks to automatic renaming (actually, for this purpose everything is done inside the monad Q
) , inside the quotation there will be no conflicts of local variable names between different inserts of the same code. The following definition demonstrates this well: summ ∷ Int → Q Exp summ n = summ' n [| 0 |] summ' ∷ Int → Q Exp → Q Exp summ' 0 code = code summ' n code = [| λx → $(summ' (n-1) [| $code + x |]) |]
n
parameters that summarizes them. For example, $(summ 3)
converted to (λx1 → λx2 → λx3 → 0 + x1 + x2 + x3)
. Notice that the generated code uses different identifiers for the arguments of nested lambda expressions, although the pattern has one name: [| λx → … |]
[| λx → … |]
. As can be seen in this example, nesting of quotes and inserts can be any, but it is important that they alternate - you cannot quote inside the quotation and paste inside the insert.[p| … |]
[p| … |]
does not rename those variables that the generated sample introduces (since these variables will be linked when matching with the sample and if they have new arbitrary names, it is not clear how to access them) .[| map |]
[| map |]
will be converted to “ GHC.Base.map
”, and a quote like [t| [String] → Bool |]
[t| [String] → Bool |]
converted to “ [GHC.Base.String] → GHC.Bool.Bool
”. If you need identifiers precisely from the scope in which the template will be pasted, use the $(dyn "…")
wrapper for them. It should be understood that this way you can accidentally use an identifier defined locally by someone else and a conflict will arise or the template will generate the wrong code, which is why it is written in the dyn
documentation that this is not a hygienic function.let x = 5 in [| … x … |]
let x = 5 in [| … x … |]
will be converted to let x = 5 in [| … $(lift x) … |]
let x = 5 in [| … $(lift x) … |]
- that is, you do not need to manually wrap the local variable identifier in type Q Exp
.Exp
structure into a Haskell code, and the other Haskell code into an Exp
structure, so they mutually annihilate: $( [| |] ) ≡ [| $( ) |] ≡
$(summ 3)
” $(summ 3)
. We will simply replace the use of the template with its definition: $(summ 3) $(summ' 3 [| 0 |]) $([| λx → $(summ' (3-1) [| $([| 0 |]) + x |]) |])
$([| … |])
brackets $([| … |])
, replacing the “ x
” along the way with a unique identifier: λx1 → $(summ' (3-1) [| 0 + x1 |])
summ'
: λx1 → $([| λx → $(summ' (2-1) [| $([| 0 + x1 |]) + x |]) |])
λx1 → λx2 → $( summ' (2-1) [| 0 + x1 + x2 |] ) λx1 → λx2 → $([| λx → $(summ' (1-1) [| $([| 0 + x1 + x2 |]) + x |]) |]) λx1 → λx2 → λx3 → $(summ' (1-1) [| 0 + x1 + x2 + x3 |]) λx1 → λx2 → λx3 → $( [| 0 + x1 + x2 + x3 |]) λx1 → λx2 → λx3 → 0 + x1 + x2 + x3
λx1 → λx2 → …
) is recursively constructed as the recursion unfolds, and the right part ( 0 + x1 + …
) at the same time accumulates in the remaining part of the pattern. The same technique is used in the printf
sample template.printf
template, which was mentioned in the first part of the article. The following is a code with explanations, as well as a Main module that uses it. You can ghc -XTemplateHaskell --make Main.hs
it with ghc -XTemplateHaskell --make Main.hs
{-# LANGUAGE TemplateHaskell #-} module Main where -- printf import Printf (printf) -- $( … ) -- Haskell- -- – putStrLn main = putStrLn ( $(printf "Error in file %s line %d: %s") "io.cpp" 325 "printer not found" )
{-# LANGUAGE TemplateHaskell #-} module Printf where -- Template Haskell import Language.Haskell.TH -- data Format = D -- "%d" – | S -- "%s" – | L String -- (L Literally) -- – Format parse :: String -> String -> [Format] parse "" rest = [L rest] parse ('%':'d':xs) rest = L rest : D : parse xs "" parse ('%':'s':xs) rest = L rest : S : parse xs "" parse (x:xs) rest = parse xs (rest++[x]) -- Haskell-, -- - gen :: [Format] -> ExpQ -> ExpQ gen [] code = code gen (D : xs) code = [| \x -> $(gen xs [| $code ++ show x |]) |] gen (S : xs) code = [| \x -> $(gen xs [| $code ++ x |]) |] gen (L s : xs) code = gen xs [| $code ++ s |] -- , -- printf :: String -> ExpQ printf s = gen (parse s "") [| "" |]
Source: https://habr.com/ru/post/132679/
All Articles