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

时间:2011-03-29 17:18:36

标签: 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'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?

感谢。

4 个答案:

答案 0 :(得分:13)

我认为你可以通过GHC支持的ScopedTypeVariables来做到这一点。这肯定会编译:

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

请注意“forall a。”

答案 1 :(得分:4)

还有另一种解决方法。不是在内部函数f中引用bar,而是将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中的类型参数更改为b,并重命名其参数。

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

Haskell抱怨因为bar注释为b -> b(对于任意类型b),但f x正在尝试应用b类型的参数到类型a -> a的函数(对于特定的,绑定的a)。换句话说,内部函数不像它的类型注释那样通用。

f传递给bar意味着对于表达式(bar f),类型变量ba绑定的类型相同。

更简单

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

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

答案 2 :(得分:2)

如果您不想使用ScopedTypeVariables扩展名,

This answer另一个问题会显示一个技巧。

答案 3 :(得分:1)