如何将 [TExp a] 转换为 TExp [a],或者以其他方式以编程方式将 refineTH 应用于多个值?

How can I turn a [TExp a] into a TExp [a], or otherwise apply refineTH to multiple values programatically?

我最近在 Haskell 中使用 refined 进行优化类型,遇到了一个主要的可用性问题。我不知道如何在编译时优化整个值列表。

例如我可以写:

{-# LANGUAGE TemplateHaskell #-}

import Refined

oneToThree :: [Refined Positive Int]
oneToThree = [$$(refineTH 1), $$(refineTH 2), $$(refineTH 3)]

但是我不能这样做排除了使用范围语法的能力,因为 Refined 没有(出于充分的理由)有 Enum.

的实例

我希望能够做类似

的事情
oneToThree :: [Refined Positive Int]
oneToThree = $$(traverse refineTH [1..3])

但我无法编译它,因为我无法将 [TExp (Refined Positive Int)] 提升到 TExp [Refined Positive Int]

是否有我缺少的模板 haskell 魔法可以让我这样做?

如果有人有建议,我们也会接受关于更好的轻量级优化类型库的建议。

这有效(但由于阶段限制,它需要位于与您使用它不同的文件中):

import Language.Haskell.TH.Syntax (Exp(ListE), TExp(TExp))

makeTypedTHList :: [TExp a] -> TExp [a]
makeTypedTHList xs = TExp $ ListE [x | TExp x <- xs]

然后您可以像这样使用它:

{-# LANGUAGE TemplateHaskell #-}

import Refined
import AboveCodeInSeparateModuleBecauseOfStageRestriction (makeTypedTHList)

oneToThree :: [Refined Positive Int]
oneToThree = $$(makeTypedTHList <$> traverse refineTH [1..3])

但是,自己调用 TExp 构造函数会破坏类型化模板 Haskell 的某些安全性(尽管我认为这种特殊情况是安全的)。理想情况下,我更喜欢不需要这样做的方法,但我想不出一个。

sequenceQTExpList :: [Q (TExp a)] -> Q (TExp [a])
sequenceQTExpList [] = [|| [] ||]
sequenceQTExpList (x:xs) = [|| $$(x) : $$(sequenceQTExpList xs) ||]

然后用作

$$(sequenceQTExpList $ map refineTH [1..3])

你说得对,感觉像是穿越。但是,该类型有点偏离,额外的 Qs 浮动。我没有立即看到任何可以让您有用地组合这些层的东西。

不幸的是,那里使用的很多机制都是 TH 语法而不是函数。只是没有一种明显的方法可以将提升和拼接作为函数进行,因此您不得不为每种容器类型编写定制的帮助程序,而不是开始使用 Traversable。这是一个有趣的问题。如果有一个干净的解决方案,如果它被提交给维护者,它很有可能成为模板 Haskell 的未来版本。但是我现在看不到它。