我有一个函数foo
并受到一连串的限制。当然,这些约束必须出现在使用foo
的函数的签名中。 ,所以我想做的就是包装 foo
类型同义词中的约束 FooCtx a b ... :: Constraint
。举个例子,
foo :: (A a, B b, C c, ...) => a -> b -> c
bar :: (A a, B b, C c, ...) ...
bar = ... foo ...
会变成
type FooCtx a b c ... = (A a, B b, C c, ...)
foo :: (FooCtx a b c) => a -> b -> c
bar :: (FooCtx a b c) => ...
如果所有类型都公开,这会非常有效。但是,我使用函数依赖关系来生成约束列表中的某些类型,并且这些类型不会出现在 foo
的签名中。例如:
class Bar a b | a -> b
foo (Bar a b, ...) => a -> a
GHC 不会接受 type FooCtx a = (Bar a b)
因为b
不受 LHS 约束。我也不能使用type FooCtx a b = (Bar a b)
因为b
不在 foo
签名的范围内。 foo
的签名将是foo :: (FooCtx a ?) => a -> a
.
一个不令人满意的解决方案是将约束之一放在 foo
中。签名为FooCtx
将fundep类型纳入范围:
class Bar a b | a -> b
type FooCtx a b = ...
foo (Bar a b, FooCtx a b) => a -> a
但这违背了对约束进行分组的目的:
在遇到这种情况之前,我认为约束同义词可以盲目地替换任意约束列表。我知道封装这样的约束的唯一其他方法是使用类,但它遇到了同样的问题:class (A a, B b, C c, ...) => FooCtx a b c
LHS 上不能有任何隐藏类型。有没有其他方法可以完全收集所有这些约束?
最佳答案
您误解了类型变量的绑定(bind)方式。它们不受 tau 类型(示例中的 a -> a
)绑定(bind),而是基于完整 phi 类型的隐式绑定(bind)器 ((Bar a b ) => a -> a
)。可以使用 ExplicitForAll
GHC 语言扩展来明确此绑定(bind)。
在您的示例中,当您编写类似的内容时
foo :: (Bar a b) => a -> a
然后,完整的 sigma 类型(具有明确的 tyvar 绑定(bind)拼写)如下(因为在隐式情况下,来自 phi 类型的所有 tyvar 都绑定(bind)在此处)
foo :: forall a b. (Bar a b) => a -> a
这意味着以相同的方式使用约束别名没有问题:如果您有例如
type FooCtx a b = (Bar a b, Num a, Eq a)
那么以下是有效的类型签名:
foo' :: forall a b. (FooCtx a b) => a -> a
因此,以下简写也是有效的:
foo' :: (FooCtx a b) => a -> a
关于haskell - 与fundeps捆绑约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28823727/