用fundeps捆绑约束

我有一个功能foo与一系列的约束。 当然,这些约束必须出现在使用foo的函数的签名中,所以我想要做的就是将foo约束包装在类型同义词FooCtx ab ... :: 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 ab)因为b没有绑定在LHS上。 我也不能使用type FooCtx ab = (Bar ab)因为b不在foo的签名范围内。 foo的签名是foo :: (FooCtx a ?) => a -> a

一个令人不满意的解决方案是使用FooCtxfoo签名中的一个约束放入范围中:

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 abc不能有任何隐藏类型在LHS上。 有没有其他方法可以完全收集所有这些限制?


你误解了类型变量是如何绑定的。 它们不是由tau蛋白结合型(与a -> a在你的例子),但基于全披类型的隐式粘合剂( (Bar ab) => a -> a )。 可以使用ExplicitForAll GHC语言扩展来明确该绑定。

在你的例子中,当你写类似的东西

foo :: (Bar a b) => a -> a

那么具有明确的tyvar-binding明确的完整sigma类型如下(因为在隐式情况下,来自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

这个问题可以通过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一起使用的a 。 我已经添加了一个额外的函数来编写示例foo'

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

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

foo' :: (A a, Bar a, B (BofA a), C c) => a -> a -> c
foo' x y = foo x (aToB y)
链接地址: http://www.djcxy.com/p/43077.html

上一篇: Bundling constraints with fundeps

下一篇: When can GHC infer constraint variables?