Haskell wiki on ScopedTypeVariables没有描述如何在类型类及其实例之间处理类型变量的范围。
以下编译
{-# 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与类型类很好地协作是不是更有意义?
答案 0 :(得分:2)
在实例定义中,您不使用作用域类型变量。在那里,您只需在idb
块中为ida
和where
提供注释,并且两者都被推广为相同的类型,即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"