如何在 TemplateHaskell 引用中捕获类型变量
How to capture type variable in TemplateHaskell quote
我正在尝试编写一个 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
我很确定这不能通过直接引用类型来完成以使用类型应用程序。当拼接代码引用 a
时,定义它的范围已不存在。
但我相信这是导致创建类型化模板的已知问题之一 Haskell。这可以用来解决这个问题。注意GHC 8.10在class、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 之前是这样。
template-haskell 似乎没有提供类似的东西。但是可能有一个解决方案可以让您从头开始实施。这个想法是定义一个 class 来携带代表每种类型的引号:
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 可以解决这个问题。
我正在尝试编写一个 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
我很确定这不能通过直接引用类型来完成以使用类型应用程序。当拼接代码引用 a
时,定义它的范围已不存在。
但我相信这是导致创建类型化模板的已知问题之一 Haskell。这可以用来解决这个问题。注意GHC 8.10在class、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 之前是这样。
template-haskell 似乎没有提供类似的东西。但是可能有一个解决方案可以让您从头开始实施。这个想法是定义一个 class 来携带代表每种类型的引号:
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 可以解决这个问题。