Haskell 如何在内部类型声明中重用类型变量

Haskell 如何在内部类型声明中重用类型变量,haskell,Haskell,作为Haskell学习过程的一部分,我喜欢显式地键入函数的类型声明。我希望能够为where子句中定义的函数这样做,但我不知道如何指定where子句中的类型变量应该表示与外部类型声明中的某个类型变量相同的类型。例如,以下代码: foo :: (a -> a) -> a -> a foo f arg = bar arg where bar :: a -> a bar a = f a 产生以下错误: src\Test.hs:7:14: Couldn

作为Haskell学习过程的一部分,我喜欢显式地键入函数的类型声明。我希望能够为where子句中定义的函数这样做,但我不知道如何指定where子句中的类型变量应该表示与外部类型声明中的某个类型变量相同的类型。例如,以下代码:

foo :: (a -> a) -> a -> a
foo f arg = bar arg
  where
    bar :: a -> a
    bar a = f a
产生以下错误:

src\Test.hs:7:14:
    Couldn't match expected type `a' against inferred type `a1'
      `a' is a rigid type variable bound by
          the type signature for `foo' at src\Test.hs:3:8
      `a1' is a rigid type variable bound by
           the type signature for `bar' at src\Test.hs:6:11
    In the first argument of `f', namely `a'
    In the expression: f a
    In the definition of `bar': bar a = f a
如何表示bar的第一个参数与foo的第二个参数的类型相同,以便对其应用f

谢谢。

对于另一个问题,如果您不想使用ScopedTypeVariables扩展,可以使用一个技巧。


我认为您可以在GHC支持的一般情况下做到这一点。这当然包括:

{-# LANGUAGE ScopedTypeVariables #-}
foo :: forall a. (a -> a) -> a -> a
foo f arg = bar arg
  where
    bar :: a -> a
    bar a = f a

请注意“forall a.”

还有另一种解决方法。扩展
bar
以接受
f
作为第一个参数,并在父函数中使用部分应用程序,而不是在内部函数
bar
中引用
f

foo :: (a -> a) -> a -> a
foo f arg = (bar f) arg
  where
    bar :: (a -> a) -> a -> a
    bar f a = f a
它不需要ScopedTypeVariables或显式类型检查代码作为其他答案

解释 为了清楚起见,让我们将
bar
中的type参数更改为
b
,并重命名其参数

foo :: (a -> a) -> a -> a
foo f arg = bar arg
  where
    bar :: b -> b
    bar x = f x
Haskell抱怨说,因为
bar
被注释为
b->b
(对于任意类型的
b
),但是
fx
试图将
b
类型的参数应用于
a->a
类型的函数(对于特定的绑定
a
)。换句话说,内部函数并不像其类型注释所宣传的那样通用

f
传递到
bar
意味着对于表达式
(bar f)
,类型变量
b
绑定到与
a
相同的类型

更简单 最后,在不改变任何其他内容的情况下,如果您愿意省略内部函数
bar
的类型签名,Haskell将按照您想要的方式推断其类型。也就是说,由于
bar
从父函数
foo
应用
f
,因此
bar
的类型将重用
foo
类型中的类型参数
a

foo :: (a -> a) -> a -> a
foo f arg = bar arg
  where
    -- Type: bar :: a -> a
    bar a = f a