Ich werde einen Versuch wagen, aber TH kann schwer zu debuggen sein, ohne mehr Code zu sehen.
Werfen wir einen Blick auf einen Beispielcode:
foo.hs:
{-# Language TemplateHaskell #-}
baz x = let f y = x + y
in [| f |]
bez x = let f y = x + y
in [| \y -> f y |]
boz x = [| \y -> x + y |]
g x y = x + y
byz x = [| g x |]
Jetzt können wir dies in GHCi starten (ich verwende Version 7.0.2, die mit der aktuellen Haskell-Plattform ausgeliefert wird):
$ ghci foo.hs -XTemplateHaskell
*Main> :m +Language.Haskell.TH
*Main Language.Haskell.TH> runQ (baz 2)
<interactive>:1:7:
No instance for (Language.Haskell.TH.Syntax.Lift (a0 -> a0))
arising from a use of `baz'
Possible fix:
add an instance declaration for
(Language.Haskell.TH.Syntax.Lift (a0 -> a0))
In the first argument of `runQ', namely `(baz 2)'
In the expression: runQ (baz 2)
In an equation for `it': it = runQ (baz 2)
*Main Language.Haskell.TH> runQ (bez 2)
<interactive>:1:7:
No instance for (Language.Haskell.TH.Syntax.Lift (a0 -> a0))
arising from a use of `bez'
Possible fix:
add an instance declaration for
(Language.Haskell.TH.Syntax.Lift (a0 -> a0))
In the first argument of `runQ', namely `(bez 2)'
In the expression: runQ (bez 2)
In an equation for `it': it = runQ (bez 2)
*Main Language.Haskell.TH> runQ (boz 2)
LamE [VarP y_0] (InfixE (Just (LitE (IntegerL 2))) (VarE GHC.Num.+) (Just (VarE y_0)))
*Main Language.Haskell.TH> runQ (byz 2)
AppE (VarE Main.g) (LitE (IntegerL 2))
Was ich hier getan habe, ist der Versuch, die runQ
um zu sehen, wie der TH-Spleiß für jede meiner Funktionen im Beispielcode aussieht. Es schlägt fehl bei baz
y bez
, sondern funktioniert für boz
y byz
.
Betrachtet man die TH für boz
y byz
können wir sehen, wie Funktionen angehoben werden: boz
ist im Grunde nur ein Verweis auf +
mit Namen (in VarE GHC.Num.+
), während byz
bezieht sich nur auf g
mit Namen (in VarE Main.g
).
Para baz
y bez
steht diese Option nicht zur Verfügung: Beide Funktionen versuchen, die f
die lokal gebunden ist; daher ist der Verweis auf VarE f
würde keinen Sinn machen außerhalb von baz
y bez
.
Was soll ein Entwickler also tun? Kurz gesagt: Anstatt zu versuchen [| f |]
müssen Sie den Ausdruck für f
im Aufzug direkt in Form von Identifikatoren, die dort gebunden werden, wo der Spleiß auftritt.
Nebenbei bemerkt, ist es sehr einfach zu schreiben Lift
Instanzen für algebraische Datentypen, da Sie global definierte Funktionen immer aufheben können. Hier ist eine für Maybe
:
instance Lift a => Lift (Maybe a) where
lift Nothing = [| Nothing |]
lift (Just a) = [| Just a |]