Reputation: 48580
Normally, when using Template Haskell, names of bindings and data constructors are quoted by prefixing them with a single quote:
showName, justName :: Name
showName = 'show
justName = 'Just
Unfortunately, this doesn't work for names whose second character is a single quote, because two single quotes with a single character between them are interpreted as a character literal. How can I work around this?
Upvotes: 3
Views: 210
Reputation: 48580
Edit: It seems the user's guide is wrong about there not being an escape mechanism! You can just add a space after the initial single quote. So... don't use the below hack.
It's possible to work around this limitation using expression quoting and a bogus Quote
instance.
{-# LANGUAGE DerivingVia #-}
module ExtractName (extractName) where
import Data.Functor.Identity
import GHC.Stack
import Language.Haskell.TH.Syntax
newtype Id a = Id {unId :: a}
deriving (Functor, Applicative, Monad) via Identity
extractName :: HasCallStack => Id Exp -> Name
extractName m = case unId m of
VarE x -> x
ConE x -> x
_ -> withFrozenCallStack $ error extractNameError
-- This is bogus, but good enough for what we're doing.
instance Quote Id where
newName _ = withFrozenCallStack $ error extractNameError
extractNameError :: String
extractNameError =
"extractName: the argument must be an expression quote containing a\n"
++ "single bare name, such as [| f'1 |]"
Now you can write, for example,
f' :: Int
data Foo = B'ar
f'Name, b'arName :: Name
f'Name = extractName [| f' |]
b'arName = extractName [| B'ar |]
How does this work? An expression quote will produce an Exp
in an arbitrary monad implementing Quote
. In general, desugaring expression quotes may require the newName
method, to desugar things like let
s and lambda expressions. However, it does not need newName
to desugar a plain old binding or data constructor. So we can write a bogus Quote
implementation for an Identity
-like type that will work for the sorts of quoted expressions we need. Once we've unwrapped the expression, we can extract the name from it.
Upvotes: 2