编译器无法决定`take`函数的返回值的类型

时间:2018-03-10 12:20:11

标签: haskell functional-programming

我试图解决the 99 problems in Haskell,而对于第4个问题,我首先尝试了这样的解决方案

myLength :: [a] -> Int
myLength [] = 0
myLength ys = go 1 ys
 where
  go :: Int -> [a] -> Int
  go n xs
   | ( (take n xs) == (take (n+1) xs) ) = n
   | otherwise = go (n+1) xs

但是,编译器会给出错误:

Problem4.hs:10:8: error:
    • No instance for (Eq a1) arising from a use of ‘==’
      Possible fix:
        add (Eq a1) to the context of
          the type signature for:
            go :: forall a1. Int -> [a1] -> Int
    • In the expression: ((take n xs) == (take (n + 1) xs))
      In a stmt of a pattern guard for
                     an equation for ‘go’:
        ((take n xs) == (take (n + 1) xs))
      In an equation for ‘go’:
          go n xs
            | ((take n xs) == (take (n + 1) xs)) = n
            | otherwise = go (n + 1) xs
   |
10 |    | ( (take n xs) == (take (n+1) xs) ) = n
   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

据我所知,错误的原因是当我们尝试比较从(take n xs)(take (n+1) xs)返回的列表时,编译器事先并不知道列表的类型,所以它无法比较它们,这就是为什么它会抱怨,所以在这一行之前,我需要告诉编译器两个返回值是相同的类型,类型是[a],但我们怎么能这样做呢? / p>

混淆:当我们指定go的类型签名时,我们明确地修复了xs的类型,即不应该这样做函数take返回的列表具有相同的类型,即[a],因此编译器是否应该能够比较它们?

修改

请注意,我在函数的定义中有另一个函数,并且有很多东西与标记为重复的问题不同,正如您所观察到的,对该问题的给定答案并不完全解决了这个问题。

2 个答案:

答案 0 :(得分:2)

您需要的是实例上下文(此处为Eq a),由=>表示:

myLength :: Eq a => [a] -> Int
myLength [] = 0
myLength ys = go 1 ys
 where
  go :: Eq a => Int -> [a] -> Int
  go n xs
   | ( (take n xs) == (take (n+1) xs) ) = n
   | otherwise = go (n+1) xs

但这不是问题#4的正确答案,因为它为函数添加了额外的约束。

编辑:对于问题“不应该每个列表都具有可比性吗?”
如果列表的元素具有可比性,则列表具有可比性。例如,函数Kleisli箭头,WrappedArrow s不具有可比性,因此不是它们的列表。

答案 1 :(得分:1)

{-# Language ScopedTypeVariables #-}

myLength :: forall a. Eq a => [a] -> Int
myLength [] = 0
myLength ys = go 1 ys
 where

  go :: Int -> [a] -> Int
  go n xs
   | take n xs == take (n+1) xs = n
   | otherwise = go (n+1) xs