How to replicate the behaviour of 'name in a T

2019-07-12 12:53发布

问题:

Consider this Haskell file:

{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
module Text (main) where

import Test.Inspection

import Data.Text as T
import Data.Text.Encoding as E
import Data.ByteString (ByteString)

import Language.Haskell.TH

toUpperString :: String -> String
toUpperString = T.unpack . T.toUpper . T.pack

toUpperBytestring :: ByteString -> String
toUpperBytestring = T.unpack . T.toUpper . E.decodeUtf8

do Just n <- lookupValueName "toUpperString"
   inspect $ n `hasNoType` ''T.Text
inspect $ 'toUpperBytestring `hasNoType` ''T.Text

main :: IO ()
main = return ()

It uses Template Haskell to define test obligations which are tested by the GHC plugin in inspection-testing.

Under the hood, inspection testing passes a Template Haskell name in an annotation, and converts it to to a Core name using thNameToGhcName, and then tries to find this name in the current module.

This works fine if I create a template Haskell name using 'foo, but it fails if I use lookupValueName.

  • Why are they different?
  • How can I recreate the behavior of 'foo when I have a string?

(Why do I not just use 'foo? Because I want to dynamically create obligations, so I have Template Haskell code that defines many such functions and obligations.)

回答1:

The problem in this case is not so much that the Name returned by 'foo vs. lookupValueName is different, but that using 'foo keeps foo alive throughout the compilation of the module, while lookupValueName "foo" does not. Then GHC removes the binding of "foo" and inspection-testing trips over it.

And indeed removing the export list from the module, which keeps all top-level bindings around, makes the problem go away.