【问题标题】:Adding type signature to where statement causes error I don't understand将类型签名添加到 where 语句会导致错误我不明白
【发布时间】:2011-06-21 03:41:50
【问题描述】:

我正在处理 yesod 中的一个复杂函数。它在 where 部分有很多没有类型的函数,但类型检查正确。我决定尝试添加一些类型签名,这样我就可以一次弄清楚发生了什么,但是添加类型签名会导致类型错误。

所以我将函数缩减为一个简单的案例来发布在这里,它仍然给出了一个我不明白的类似错误。

helper :: [(String, a)] -> [(Int, a)]
helper xs = blah
  where
    blah :: [(Int, a)]
    blah = zip [1..10] (map snd xs)

如果我从 blah 中删除类型签名,它编译得很好,但如果我添加该类型签名,它会给我错误:

Couldn't match type `a' with `a1'
  `a' is a rigid type variable bound by
      the type signature for helper :: [(String, a)] -> [(Int, a)]
      at Blah.hs:4:1
  `a1' is a rigid type variable bound by
       the type signature for blah :: [(Int, a1)] at Blah.hs:7:5
Expected type: [(String, a1)]
  Actual type: [(String, a)]
In the second argument of `map', namely `xs'
In the second argument of `zip', namely `(map snd xs)'
  1. 我也不知道为什么在进行类型检查时,助手中的“a”被解释为与助手不同的“a”。
  2. 为什么它甚至会关心 a 是否不同
  3. 我不知道如何确定 blah 实际是什么类型,因为我无法在仍使用其参数的同时将其移至顶层。

编辑:

好的,在我将此标记为已回答之前,我还要进行一次编辑。在我使用的代码中,有一些约束(Eq a,Monad monad)=> 等等,等等等等,所以我的解决方案不太奏效。所以我正在修改我的示例代码以更接近真实代码:

helper :: (Eq a, Num b) => b -> [(String, a)] -> (b, [(Int, a)])
helper b xs = (b+b, blah)
  where
    blah :: [(Int, a)]
    blah = filter (\y -> fst y == 11) $ zip [1..10] (map snd xs)

如果我放

helper :: forall a. (Eq a, Num b) => b -> [(String, a)] -> (b, [(Int, a)])

这不起作用,因为(我假设因为 b 不在范围内,但我无法弄清楚将 forall b 放入此类型的语法。(forall a,forall b 不起作用,forall a, b 不起作用)。

【问题讨论】:

    标签: haskell


    【解决方案1】:

    blah 类型中的ahelper 类型中的a 不同,除非您使用ScopedTypeVariables 扩展名。所以你的类型签名说它们是独立的,但显然不是。 你的代码相当于这个:

    helper :: forall a. [(String, a)] -> [(Int, a)]
    helper xs = blah
      where
        blah :: forall b. [(Int, b)]
        blah = zip [1..10] (map snd xs)
    

    在这里您是说对于任何给定的a,我们可以选择任何b,但事实并非如此。由于xs 的类型为[(String, a)]map snd xs 的类型为[a]。所以ab必须是同一类型,即a ~ b。因此编译器抱怨blah 不像你在类型签名中所说的那样多态。

    您有三个选择:

    • 删除类型签名。编译器将推断blah 的正确类型。

    • 启用ScopedTypeVariables。然后编译器将意识到您希望blah 类型中的ahelper 签名中的a 相同。在这种情况下,您需要在helper的类型签名中添加显式forall a.

      {-# LANGUAGE ScopedTypeVariables #-}
      helper :: forall a. [(String, a)] -> [(Int, a)]
      helper xs = blah
        where
          blah :: [(Int, a)]
          blah = zip [1..10] (map snd xs)
      
    • 使blah的类型独立:

      helper :: [(String, a)] -> [(Int, a)]
      helper xs = blah xs
        where
          blah :: [(String, b)] -> [(Int, b)] -- Or 'a'. Doesn't matter.
          blah ys = zip [1..10] (map snd ys)
      

      现在blah 将适用于任何b。仅将它与 b ~ a 一起使用这一事实非常好。

    回答编辑:

    forall 中的类型变量之间使用空格,例如

    helper :: forall a b. (Eq a, Num b) => ...
    

    【讨论】:

    • 唯一对我有用的选项,将 {-# LANGUAGE ScopedTypeVariables #- 添加到文件顶部,并不能消除错误。还是一样的。
    • @onmach:我的错,你还需要一个明确的forall。我已经更新了我的答案。
    • 几乎有效。但我还不能完全使用这个答案,所以我编辑了操作。
    • @onmach:应该是forall a b.。没有逗号,只是您要量化的类型变量之间的空格。
    • 编辑:实际上它确实有效。非常感谢,我现在可以调试了。
    猜你喜欢
    • 2016-08-11
    • 2016-02-04
    • 1970-01-01
    • 2011-06-15
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 2010-11-21
    • 1970-01-01
    相关资源
    最近更新 更多