将类型签名添加到where语句导致错误我不明白

时间:2011-06-20 18:43:26

标签: haskell

我正在使用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 a首先是不同的
  3. 我不知道如何弄清楚实际上是什么类型,因为我仍然无法在使用其参数时将其移动到顶层。
  4. 修改

    好的,在我标记为已回答之前,我还有一个编辑。在我正在使用的代码中有一些约束(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 a,forall b不起作用,forall a, b不起作用。)

1 个答案:

答案 0 :(得分:13)

a类型中的blaha类型中的helper不同,除非您使用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。然后,编译器将意识到您希望a类型中的blaha签名中的helper相同。在这种情况下,您需要在forall a.的类型签名中添加明确的helper

    {-# 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) => ...