Haskell使用where在嵌套函数定义中作用域

时间:2009-11-30 12:37:37

标签: haskell types scope type-mismatch

我对Haskell在where定义中的范围有疑问。当我有以下函数f时,我希望将x传递给本地定义的函数f1而不明确地将其用作参数,我得到一个错误,说明类型xf1输出中的x不兼容,但它应该相同:

f :: Eq a => a -> [a]
f x = f1 x
    where
        f1 :: Eq a => a -> [a]
        f1 y = [ x, y ]

错误如下:

    Couldn't match expected type `a1' against inferred type `a'
      `a1' is a rigid type variable bound by
           the type signature for `f1' at test.hs:4:11
      `a' is a rigid type variable bound by
          the type signature for `f' at test.hs:1:8
    In the expression: x
    In the expression: [x, y]
    In the definition of `f1': f1 y = [x, y]
Failed, modules loaded: none.

然而,当我将g作为附加参数传递时,正如我在以下代码中使用函数a所做的那样,它可以正常工作:

g :: Eq a => a -> [a]
g x = g1 x x
    where
        g1 :: Eq a => a -> a -> [a]
        g1 x y = [ x, y ]

有没有办法让f中的a类型与a1中的f1(或{{1}})类型兼容?

2 个答案:

答案 0 :(得分:18)

戴夫就在上方。另一种思考方式是即使你的两个类型签名都引用变量a,它实际上并不是同一个类型变量。在Haskell-prime表示法中,两个签名都可以更明确地写为:

forall a . Eq a => a -> [a]

意味着对于两个函数,它们可以接受任何类型的参数(在Eq中)。这显然不是这里的情况。在标准的Haskell 98中,唯一的选择是放弃f1的类型签名。但是GHC(和其他人?)支持lexically scoped type variables。所以你可以写

{-# LANGUAGE ScopedTypeVariables #-}

f :: forall a. Eq a => a -> [a]
f x = f1 x
    where
        f1 :: a -> [a]
        f1 y = [ x, y ]

这样可以正常工作。

答案 1 :(得分:11)

您的代码的问题是本地范围的f1类型签名。它指定f1可以采用任何类型

f1 :: Eq a => a -> [a]

即使这是一个本地函数,你已经推广了这个函数,以便能够获取f中不存在的类型,无论这个函数接收来自f的HAS,所以类型签名是不必要的。 / p>

只需删除f1类型签名。

编辑:回复自己的帖子,有点不清楚。 f1中的a是一个参数化类型,可以取任何东西,但传递给它的参数已经绑定在f中。因此,此函数只能接收其父函数接收的内容,您给它的类型签名会破坏该规则。希望更清楚一点。