类型类与其实例之间的ScopedTypeVariables

时间:2014-10-18 06:54:25

标签: haskell typeclass

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与类型类很好地协作是不是更有意义?

1 个答案:

答案 0 :(得分:2)

在实例定义中,您不使用作用域类型变量。在那里,您只需在idb块中为idawhere提供注释,并且两者都被推广为相同的类型,即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"