Haskell 模板中带单引号的名称

问题描述 投票:0回答:1

通常,使用 Template Haskell 时,绑定和数据构造函数的名称通过在它们前面加上单引号来引用:

showName, justName :: Name
showName = 'show
justName = 'Just

不幸的是,这对于第二个字符是单引号的名称不起作用,因为两个单引号之间有一个字符会被解释为字符文字。我该如何解决这个问题?

haskell template-haskell
1个回答
2
投票

编辑:似乎用户指南是错误的关于没有逃生机制!您只需在最初的单引号后添加一个空格即可。所以...不要使用下面的技巧。


可以使用表达式引用和虚假的

Quote
实例来解决此限制。

{-# 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 |]"

现在你可以写,例如,

f' :: Int
data Foo = B'ar

f'Name, b'arName :: Name
f'Name = extractName [| f' |]
b'arName = extractName [| B'ar |]

这是如何运作的?表达式引用将在实现

Exp
的任意 monad 中生成
Quote
。一般来说,对表达式引号进行脱糖可能需要
newName
方法,以对
let
和 lambda 表达式等进行脱糖。但是,它不需要需要
newName
来对普通的旧绑定或数据构造函数进行脱糖。因此,我们可以为类似
Quote
的类型编写一个虚假的
Identity
实现,该实现适用于我们需要的各种带引号的表达式。一旦我们解开表达式,我们就可以从中提取名称。

© www.soinside.com 2019 - 2024. All rights reserved.