用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
。
一个令人不满意的解决方案是使用FooCtx
将foo
签名中的一个约束放入范围中:
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
这个问题可以通过TypeFamilies
和FlexibleContexts
来解决。
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
我们有三个类, A
, B
和C
以及你原来的foo
函数。
class A a
class B a
class C a
foo :: (A a, B b, C c) => a -> b -> c
foo = undefined
Bar
类使用类型族来确定B
和a
一起使用的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