Haskell Template: GHC Level Limitations and Ways to Overcome

I have the following code in a module:

{-# LANGUAGE TemplateHaskell #-} module Alpha where import Language.Haskell.TH import Data.List data Alpha = Alpha { name :: String, value :: Int } deriving (Show) findName n = find ((== n) . name) findx obj = sequence [valD pat bod []] where nam = name obj pat = varP (mkName $ "find" ++ nam) bod = normalB [| findName nam |] 

And then in the main file there is the following:

 {-# LANGUAGE TemplateHaskell #-} import Alpha one = Alpha "One" 1 two = Alpha "Two" 2 three = Alpha "Three" 3 xs = [one, two , three] findOne = findName "One" findTwo = findName "Two" $(findx three) -- This Fails $(findx (Alpha "Four" 4)) -- This Works main = putStrLn "Done" 

I would like $(findx three) create findThree = findName "Three" for me. But instead, I get this error:

 GHC stage restriction: `three' is used in a top-level splice or annotation, and must be imported, not defined locally In the first argument of `findx', namely `three' In the expression: findx three 

How can I overcome this? I would prefer not to define one , two , etc. In a separate file.

Second question: why does $(findx (Alpha "Four" 4)) work without problems?

+6
haskell template-haskell
source share
1 answer

I am not very versed in the Haskell pattern, but based on my limited understanding, the problem is that three in a sense ā€œstill definedā€ when the GHC tries to compile $(findx three) , while all components from $(findx (Alpha "Four" 4)) already fully defined.

The main problem is that all definitions in one module affect each other's value . This is due to the type of output, as well as to mutual recursion. The definition of x = [] can mean many different things, depending on the context; it could be binding x to an Int list or an IO () list, or something else. The GHC may need to process the entire module to find out what exactly it means (or that this is actually an error).

The code that Template Haskell emits into the module that compiles should be considered by this analysis. Thus, this means that the Haskell template code must be executed before the GHC finds out what the definitions in the module mean, and therefore logically you cannot use them.

Things that were imported from other OTOH modules were already fully verified when the GHC compiled this module. There is no more information that needs to be learned about them by compiling this module. Thus, they can be accessed and used before compiling the code in this module.

Another way to think: maybe three should not really be Alpha type. Perhaps it was a typo, and the constructor should have been Alphz . Typically, the GHC learns about these types of errors by compiling all other code in a module that uses three to find out if it is inconsistent or not. But what if this code uses or is used by things that emit only $(findx three) ? We don’t even know which code will work until we run it, but we cannot decide whether three printed correctly until we run it.

Of course, this restriction could be slightly reduced in some cases (I don’t know if it would be easy or practical). Perhaps we could get the GHC to believe that something is ā€œdefined earlierā€ if it is imported, or if it uses only other things that are ā€œdefined earlierā€ (and possibly has an explicit type signature). Perhaps he can try to compile the module without running the TH code, and if he succeeds in completely executing typecheck three before he encounters any errors, he can pass this into the TH code and then recompile everything. The downside (besides working) would be much harder to specify which exact restrictions apply to what you can pass to the Haskell template.

+6
source share

All Articles