我一直在玩-XConstraintKinds
以帮助减轻过分冗长的情境,并且在变量绑定方面发现了一个有趣的不一致:
{-# LANGUAGE
TypeFamilies
, ConstraintKinds
, FlexibleContexts
#-}
-- works
foo :: ( callTree ~ SomeTypeFunc output
, OtherTypeFunc input ~ callTree
) => input -> output
type FooCtx input output =
( callTree ~ SomeTypeFunc output
, OtherTypeFunc input ~ callTree
)
-- doesn't work
foo' :: FooCtx input output =>
input -> output
除了将callTree
带到顶级范围之外,还有解决方法吗?
答案 0 :(得分:6)
没有真正的不一致,只是
类型签名中的自由类型变量会自动添加forall
个量词,因此第一种情况实际上等同于
foo :: forall callTree output input. ( callTree ~ SomeTypeFunc output
, OtherTypeFunc input ~ callTree
) => input -> output
type
声明右侧的所有自由类型变量必须是参数,不能包含任何不在范围内的变量。
这不是特定于约束种类,除了您不能将forall
量词直接应用于约束的不幸事实,这意味着我不知道一般变通方法使这种约束类型声明工作。
在您的特定示例中,( OtherTypeFunc input ~ SomeTypeFunc output )
应该是等效的,但我认为您真的对一个更复杂的例子感兴趣,而这种重写并不起作用。
我可以想到一种不同的解决方法,通过在约束的右侧包含值,以不同的方式将参数更改为类型声明:
{-# LANGUAGE TypeFamilies, ConstraintKinds, FlexibleContexts, RankNTypes #-}
type family OtherTypeFunc a
type family SomeTypeFunc a
type FooCtx input output value =
forall callTree. ( callTree ~ SomeTypeFunc output
, OtherTypeFunc input ~ callTree
) => value
-- This now works
foo' :: FooCtx input output ( input -> output )
foo' = undefined