我最近在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])
您是对的,感觉就像是遍历。但是,类型略有不同,多余的Q
随处可见。我看不到任何可以使您有效地合并这些图层的东西。
[不幸的是,这里使用的许多机制都是TH语法而不是函数。只是没有一个明显的方法可以同时完成作为函数的提升和拼接,因此您不得不为每种容器类型编写定制的帮助程序,而不必使用Traversable
。这是一个有趣的问题。如果有一个干净的解决方案,那么如果将其提交给维护人员,则很有可能将其纳入模板Haskell的未来版本中。但是我现在看不到。