【问题标题】:How to capture type variable in TemplateHaskell quote如何在 TemplateHaskell 引用中捕获类型变量
【发布时间】:2020-09-29 04:37:35
【问题描述】:

我正在尝试编写一个 Lift 实例,它不仅可以提升构造函数,还可以提升其类型变量。 例如,以Proxy a 为例。我需要一个Lift 实例,这样当lift (Proxy @Int) 被拼接时,GHC 会正确推断出生成的表达式是Proxy Int

-- GHC should infer that x :: Proxy Int
x = $(TH.lift (Proxy @Int))

我试过了:

instance Lift (Proxy a) where
  lift _ = [|Proxy @a|]

x = $(TH.lift (Proxy @Int))

似乎 TH 捕获了 a 而不是 Int 如预期的那样。 我不知道还能尝试什么

/.../TH/Test.hs:15:7: error:
    • The exact Name ‘a’ is not in scope
        Probable cause: you used a unique Template Haskell name (NameU), 
        perhaps via newName, but did not bind it
        If that's it, then -ddump-splices might be useful

【问题讨论】:

  • 我之前的评论有误。引号使用Lift 表示特定类型的变量,而不是文字构造函数。基本模式很好。
  • 生成类型应用程序真的很重要吗?我认为不可能使用未知类型进行这项工作。似乎您遇到了在生成拼接时外部类型变量不再存在的问题,因此没有什么可以引用的。有一些解决方法,但它们需要在 Lift 类中放弃。
  • 啊,如果你放弃类型应用程序,Lift 类可以工作。
  • 不需要使用类型应用程序。这是一个更接近我的实际用例的重现:gist.github.com/dcastro/20175fdbf2e43a77c9504d98e9fa3c36
  • 另外,对于其他阅读此票证的人,似乎这个问题已与 GHC 团队提出,请参阅 thisthis

标签: haskell template-haskell


【解决方案1】:

template-haskell 似乎没有提供类似的东西。但是可能有一个您可以从头开始实施的解决方案。这个想法是定义一个类来携带代表每种类型的引号:

class TLift a where
  tlift :: Q Type

例如:

instance TLift Int where
  tlift = [t|Int|]

-- and so on

然后定义一个以类型应用程序为特色的引用:

proxyQ :: forall a. TLift a => Q Exp
proxyQ = [|Proxy @( $(tlift @a) )|]

这里的一个限制是TLift 实例只能为完全具体的类型生成引号,没有类型变量。也许reflection 可以解决这个问题。

【讨论】:

  • 啊,看起来 Matthew Pickering 建议了 something like this before,尽管它被拒绝了。他称之为LiftT,建议的LiftT 实例应该由GHC 自动生成。您的建议似乎是我们目前能得到的最接近的建议,谢谢!
【解决方案2】:

我很确定这不能通过直接引用类型来完成,以便使用类型应用程序。当拼接代码引用a 时,它的定义范围已不存在。

但我相信这是导致创建类型化模板 Haskell 的已知问题之一。这可以用来解决这个问题。注意,GHC 8.10 在类中引入了一个额外的入口,liftTyped,可以直接解决这个问题。但它也提供了在早期版本的 GHC 上使用它所需的提示,即lift x === unTypeQ (liftTyped x)

由此,我可以看到如何为早于 8.10 的 GHC 版本为 Proxy a 编写 Lift 的实例。

instance Lift (Proxy a) where
    lift x = unTypeQ (helper x)
      where
        helper :: Proxy b -> Q (TExp (Proxy b))
        helper _ = [|| Proxy ||]

对于 GHC 8.10,这只是

instance Lift (Proxy a) where
    liftTyped _ = [|| Proxy ||]

老实说,似乎有很多 Lift 实例不见了。这只是更间接的写法之一,至少在 GHC 8.10 之前是这样。

【讨论】:

  • unTypeQ 似乎不起作用,GHC 仍然推断出x :: Proxy a。这是一个不同的重现,更接近我的实际用例:gist.github.com/dcastro/20175fdbf2e43a77c9504d98e9fa3c36
  • 换句话说,如果构造函数使用幻像数据类型并打包类型类dict,则提升的表达式需要类型注释或类型应用程序才能编译。但是unTypeQ 删除了我们需要的所有打字信息。
猜你喜欢
  • 2012-03-09
  • 2013-09-17
  • 2019-10-12
  • 1970-01-01
  • 2023-03-12
  • 2011-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多