【问题标题】:Haskell scoping in nested function definitions using where使用 where 嵌套函数定义中的 Haskell 范围
【发布时间】:2010-12-21 15:10:40
【问题描述】:

我对 Haskell 在 where 定义中的作用域有疑问。当我有以下函数f,我想将x 传递给本地定义的函数f1 而不明确地将其用作参数时,我收到一个错误,指出x 的类型与f1 的输出中的那个,虽然应该是一样的:

f :: 等式 a => a -> [a] f x = f1 x 在哪里 f1 :: Eq a => a -> [a] f1 y = [ x, y ]

错误如下:

无法将预期类型“a1”与推断类型“a”匹配 `a1' 是一个刚性类型变量,由 test.hs:4:11 中“f1”的类型签名 `a' 是一个刚性类型变量,由 `f' 在 test.hs:1:8 的类型签名 在表达式中:x 在表达式中:[x, y] 在 `f1' 的定义中: f1 y = [x, y] 失败,加载模块:无。

但是,当我将 x 作为附加参数传递时,就像我在下面的代码中使用函数 g 所做的那样,它可以正常工作:

g :: Eq a => a -> [a] g x = g1 x x 在哪里 g1 :: Eq a => a -> a -> [a] g1 x y = [ x, y ]

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

【问题讨论】:

  • 虽然用大量的类型签名给你的代码加糖是非常好的做法,除了极其微不足道的部分,我看不出用它们加糖本地范围函数的原因。类型签名是什么很明显,对于像这样的微不足道的函数,你失去的可读性比你得到的要多。
  • 嗯,实际的功能比较复杂,我只是把它简化为一个简单的例子,这样我的实际问题就会变得清晰。

标签: haskell types scope type-mismatch


【解决方案1】:

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

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

意味着对于 both 函数,它们可以接受任何类型的参数(在 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 ]

这样就可以了。

【讨论】:

    【解决方案2】:

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

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

    尽管这是一个本地函数,但您已经将此函数概括为能够采用 f 中不存在的类型,无论此函数接收到来自 f 的什么,因此类型签名是不必要的。

    只需删除 f1 类型签名即可。

    编辑:把我的帖子读给自己听,有点不清楚。 f1 中的 a 是可以接受任何内容的参数化类型,但传递给它的参数已经绑定在 f 中。所以这个函数只能接收它的父函数接收的东西,你给它的类型签名打破了这个规则。希望这更清楚一点。

    【讨论】:

    • 是的,这行得通,谢谢 :) 虽然它仍然让人担心无法明确地键入 f1 函数(我来自更严格的语言),但好吧,只要什么都没有;)
    • Poke:这里的问题是 Haskell 的类型比你预期的更严格。您对“f1”的原始类型签名指定参数可以是属于“Eq”类型类的任何内容,但编译器正确地看到传递给“f1”的参数实际上必须与传递给“f”的内容完全相同。删除 'f1' 的类型签名不会放松类型严格性。这样做只允许编译器为您推断类型。
    猜你喜欢
    • 1970-01-01
    • 2019-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-31
    • 2013-05-03
    相关资源
    最近更新 更多