dfeuer
dfeuer

Reputation: 48580

Names with single quotes in Template Haskell

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

Answers (1)

dfeuer
dfeuer

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 lets 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

Related Questions