ScopedTypeVariables 上的 Haskell wiki 没有描述如何在类型类与其实例之间处理类型变量的作用域。
以下编译
{-# LANGUAGE ScopedTypeVariables #-}
class A a where
f :: forall b. a -> b -> (a,b)
instance A Int where
f a b = (idb a, ida b)
where idb :: b -> b
idb = id
ida :: a -> a
ida = id
而以下没有(如预期的那样:Couldn't match expected type ‘a’ with actual type ‘b’
(
g :: forall a b. a -> b -> (a,b)
g a b = (idb a, b)
where idb :: b -> b
idb = id
既然 wiki 是沉默的,那么 ScopedTypeVariables 应该如何与类型类一起工作? 这是一个错误、错误功能还是设计使然?维基提到了一些Haskell98的解决方法,它们似乎与使用类型类兼容(因此更胜一筹(。
编辑:
如注释中所述,InstanceSigs
可以从类中重新引入签名,从而将类型变量纳入范围。 但与 Haskell98 替代方案相比,这似乎非常尴尬:
data D
instance A D where
f a b = (idb a, b)
where idb x = asTypeOf b x -- gives the correct error message
{-# LANGUAGE InstanceSigs #-}
data E
instance A E where
-- Complicated way to bring the class signature into scope..?
f :: forall b. E -> b -> (E, b)
f a b = (idb a, b)
where idb :: b -> b
idb = id
在实例声明中重用类声明中的范围以使 ScopedTypeVariables 与类型类很好地配合使用不是更有意义吗?
在实例定义中,未使用作用域类型变量。在那里,您只需为 where
块中的 idb
和 ida
提供注释,并且两者都泛化为同一类型,即 forall a. a -> a
(类型变量的模重命名(,因此它们都适用于任何类型。
此外,在实例定义中,您有A Int
,因此没有任何变量可以引用。
通常,如果我们有 ScopedTypeVariables
,实例头中的类型变量默认在实例实现中可见,否则不可见。另请注意,当它们可见时,它们可以被其他作用域类型变量(如任何其他作用域类型变量 var(遮盖。
例子:
class A a where
f :: a -> b -> (a,b)
instance A a where
f x y = (x :: a, y) -- an error without ScopedTypeVariables, fine with it.
instance A a where
f x y = (x , y) where
foo :: forall a. a -> a
foo _ = x -- an error, because we shadowed the instance "a" with the forall "a"