我正在尝试使用 constraints
包(用于 GHC Haskell)。我有一个类型族,用于确定类型级列表是否包含元素:
type family HasElem (x :: k) (xs :: [k]) where
HasElem x '[] = False
HasElem x (x ': xs) = True
HasElem x (y ': xs) = HasElem x xs
这个可以运行,但是它没有给我知道的一件事
HasElem x xs entails HasElem x (y ': xs)
因为类型族不是“属于”语句的归纳定义(就像在Agda中会有的那样)。在GADTs可以提升到类型级别之前,我相信没有办法使用数据类型表示列表成员资格。
所以,我使用了constraints
包来编写这个:
containerEntailsLarger :: Proxy x -> Proxy xs -> Proxy b -> (HasElem x xs ~ True) :- (HasElem x (b ': xs) ~ True)
containerEntailsLarger _ _ _ = unsafeCoerceConstraint
有点诡异,但它很有效。我可以在蕴含中进行模式匹配以获取所需内容。我想知道的是它是否会导致程序崩溃。似乎不可能,因为unsafeCoerceConstraint
的定义如下:
unsafeCoerceConstraint = unsafeCoerce refl
在 GHC 中,类型级别在运行时被省略。虽然我想检查一下,以确保这样做是可以的。--- 编辑 ---
由于还没有人给出解释,所以我想扩展一下问题。在我创建的不安全隐含中,我只期望有一个类型族。如果我像下面这样涉及到类型类字典,会怎么样:
badEntailment :: Proxy a -> (Show a) :- (Ord a)
badEntailment _ = unsafeCoerceConstraint
我假设这几乎肯定会导致segfault。这是真的吗?如果是,它与原始内容有何不同?
--- 编辑2 ---
我想提供一些背景,解释我为什么对此感兴趣。我之一的兴趣是在Haskell中制作可用的关系代数编码。我认为,无论您如何定义函数来处理类型级别的列表,都会有明显的未正确证明的事情。例如,我以前遇到的一个约束条件(用于半连接)看起来像这样(这是从记忆中得出的,因此可能不完全准确):
semijoin :: ( GetOverlap as bs ~ Overlap inAs inBoth inBs
, HasElem x as, HasElem x (inAs ++ inBoth ++ inBs)) => ...
所以,对于一个人来说,如果我取两个集合的并集,其中包含了一个元素 x
也在 as
中,这应该是显而易见的。但我不确定是否能够让约束求解器合法地相信这一点。这就是我使用这种技巧的动机。我创建蕴含关系来欺骗约束求解器,但我不知道这是否真的安全可靠。
as
和bs
中的位置,知道它在联合体中会有什么帮助呢?我很想看一些实际的代码。 - dfeuer