Haskell Error: \"No instance for (Enum [Int])

2019-07-19 07:00发布

问题:

I have the following code:

betaRest :: Int -> [Int] -> Int
betaRest n prevDigits | n == 0    = (length prevDigits)
                      | otherwise = (sum (map (betaRest (n - 1)) [0..9]))

betaFirst :: Int -> Int
betaFirst n | n == 0    = 0
            | otherwise = (betaRest (n - 1) [1..9])

It gives me the following errors, and I don't know why.

1) No instance for (Enum [Int]) arising from the arithmetic sequence '0 .. 9'

2) No instance for (Num [Int]) arising from the literal '0'

Does Haskell think that things made with the ".." operator are enumerations? But why isn't there an error for the line that's 4 lines below (with "[1..9]") it then?


Edit: What I want the code to do is like this (procedurally):

int betaRest(int n, int[] prevDigits) {
  if (n == 0) return prevDigits.length;
  else {
    sum = 0;
    foreach prevDigit in prevDigits {
      sum += betaRest(n - 1, [0..9]);
    }
    return sum;
  }
}

int betaFirst(int n) {
  if (n == 0) return 0;
  else return betaRest(n - 1, [1..9]);
}

Thus, betaFirst(1) == 9, and betaFirst(2) == 90. Yes, somebody may want to suggest a formula for generating this, but I'm going to add a filter of some sort to [0..9], thus reducing the range.

回答1:

You pass betaRest to map. Map is (a -> a) -> [a] -> [a] so for [Int] list you pass it it wants an Int -> Int function. But partially applied betaRest is [Int] -> Int.

As for [0..9] its type is (Enum t, Num t) => [t] and it's translated into enumFromTo 0 9 application. So compiler figured your error the other way around: if you define special Num and Enum instances for lists, so [0..9] becomes a list of lists of int, then your application will make sense.

But I think you want to use inits or tails function. Let us know what you want to achieve so we can help with solution.

A minimal fix to would be to add prevDigits as an argument to map and use a lambda abstraction to ignore unused prevDigit:

   | otherwise = sum (map (\prevDigit -> betaRest (n - 1) [0..9]) prevDigits)


回答2:

(sum (map (betaRest (n - 1)) [0..9]))

Let's reduce the number of parentheses to better be able to see what happens.

sum (map (betaRest (n - 1)) [0..9])

The argument of sum is

map (betaRest (n-1)) [0 .. 9]

Now, betaRest :: Int -> [Int] -> Int, hence the type of the partially applied function is

betaRest (n-1) :: [Int] -> Int

hence we can infer the type

map (betaRest (n-1)) :: [[Int]] -> [Int]

But the argument passed to map (betaRest (n-1)) is [0 .. 9], which has type

[0 .. 9] :: (Num a, Enum a) => [a]

The Num constraint comes from the use of an integer literal, and the Enum constraint from the use of the enumFromTo function (in its syntax-sugared form [low .. high]). Passing that as an argument to a function expecting an argument of type [[Int]] means the type variable a must be instantiated as [Int], and then the constraints need to be checked, i.e. the instances of [Int] for Num and Enum must be looked up. Neither of these exist, hence the error messages.

I'm not sure your procedural example is really what you want,

int betaRest(int n, int[] prevDigits) {
  if (n == 0) return prevDigits.length;
  else {
    sum = 0;
    foreach prevDigit in prevDigits {
      sum += betaRest(n - 1, [0..9]);
    }
    return sum;
  }
}

the foreach loop is much easier (and more efficiently) expressed as

sum = prevDigits.length * betaRest(n-1, [0 .. 9]);

so for the foreach to make sense, there should be a dependence on prevDigit in the loop body.

The translation to Haskell would be

betaRest n prevDigits
    | n == 0    = length prevDigits
    | otherwise = length prevDigits * betaRest (n-1) [0 .. 9]
    -- or with the loop, with the small improvement that `betaRest (n-1) [0 .. 9]
    -- is only computed once (if the Haskell implementation is sensible)
    -- | otherwise = sum $ map (const $ betaRest (n-1) [0 .. 9]) prevDigits

But as stated above, I doubt that's really what you want.