类型类与其实例之间的作用域类型变量



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 块中的 idbida 提供注释,并且两者都泛化为同一类型,即 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"

最新更新