用fundeps捆绑约束

时间:2015-03-03 03:45:37

标签: haskell ghc functional-dependencies type-constraints

我有一个带有一连串限制的函数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

但这违背了对约束进行分组的目的:

在遇到这种情况之前,我假设Constraint同义词可以盲目地替换任意约束列表。我知道封装这样的约束的另一种方法是使用类,但它遇到同样的问题:class (A a, B b, C c, ...) => FooCtx a b c在LHS上不能有任何隐藏类型。还有其他方法可以完全收集所有这些约束吗?

2 个答案:

答案 0 :(得分:5)

您误解了类型变量的绑定方式。它们是受tau类型(示例中为a -> a)的约束,但是基于完整的phi类型((Bar a b) => a -> a)的隐式绑定器。可以使用ExplicitForAll GHC语​​言扩展来明确此绑定。

在您的示例中,当您编写类似

的内容时
foo :: (Bar a b) => a -> a

然后完整的sigma类型,明确的tyvar绑定拼写出来,如下(因为在隐式的情况下,来自phi类型的所有tyvars都绑定在这里)

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

答案 1 :(得分:1)

TypeFamiliesFlexibleContexts可以解决此问题。

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}

我们有三个类,ABC以及您原来的foo函数。

class A a
class B a
class C a

foo :: (A a, B b, C c) => a -> b -> c
foo = undefined

Bar类使用类型系列来确定Ba的关系。我添加了一个额外的函数,只是为了编写示例foo'

class Bar a where
    type BofA a :: *
    aToB :: a -> BofA a

foo'是一个函数,它没有B的任何输入或输出,但在其实现中仍然使用foo。它要求与BofA关联的a类型满足B约束。此签名需要灵活的上下文。

foo' :: (A a, Bar a, B (BofA a), C c) => a -> a -> c
foo' x y = foo x (aToB y)