0
votes

I'm trying to implement the luhn algorithm using the following code:

luhn :: Int -> Bool
luhn x = (tail $ show (foldl (\acc x -> acc + (read x :: Int)) 0 (foldr doEncrypt [] $ zip [0..] (show x)))) == "0"
    where
        doEncrypt (i,y) acc = if not(even i)
            then head(((uncurry (+) . (`divMod` 10) . (*2)) y)) : acc
            else (head y) : acc

I now got stuck with the following error:

• Non type-variable argument in the constraint: Integral [a2]
  (Use FlexibleContexts to permit this)
• When checking the inferred type
    doEncrypt :: forall a1 a2.
                 (Integral a1, Integral [a2]) =>
                 (a1, [a2]) -> [a2] -> [a2]
  In an equation for ‘luhn’:
      luhn x
        = (tail
             $ show
                 (foldl
                    (\ acc x -> acc + (read x :: Int))
                    0
                    (foldr doEncrypt [] $ zip [0 .. ] (show x))))
            == "0"
        where
            doEncrypt (i, y) acc
              = if not (even i) then
                    head (((uncurry (+) . (`divMod` 10) . (* 2)) y)) : acc
                else
                    (head y) : acc

I see that the error suggests that the second part of the tuple (a2) is a "Non type-variable argument". However, Haskell seems to identify this a2 argument as an Integral while in fact it is a Char. How can I tell Haskell that this is a Char and that Haskell shouldn't worry any further about this variable's type? Or is there something else I'm not understanding which causes this error?

Edit: When I remove the (head y) and replace it by y I get instead the following error:

• Couldn't match type ‘Char’ with ‘[Char]’
  Expected type: [String]
    Actual type: [Char]
• In the third argument of ‘foldl’, namely
    ‘(foldr doEncrypt [] $ zip [0 .. ] (show x))’
  In the first argument of ‘show’, namely
    ‘(foldl
        (\ acc x -> acc + (read x :: Int))
        0
        (foldr doEncrypt [] $ zip [0 .. ] (show x)))’
  In the second argument of ‘($)’, namely
    ‘show
       (foldl
          (\ acc x -> acc + (read x :: Int))
          0
          (foldr doEncrypt [] $ zip [0 .. ] (show x)))’
1
Since you use (head y), that means that y should be a list, but in other parts of the function body, you use y as a number (to perform calculations on). The result is that Haskell thinks that the list should be a number, hende the error. - Willem Van Onsem
@WillemVanOnsem Thanks for the response. I tried that, but that results in another error I do not understand and I cannot find information on Google about (I appended it to my question). - Simon Baars
Well I think you should add a head in the first clause instead, as well a a read. But that being said. This looks very "chaotic". Even if it later "produces an answer", it is hard to "convince" a peer that the algorithm works correctly. So I think it might be be better to implement a cleaner solution. - Willem Van Onsem
@WillemVanOnsem This actuallly helped me solve it. Thanks! - Simon Baars

1 Answers

3
votes

There where multiple things wrong with my solution, but finally the following code works!

luhn :: Int -> Bool
luhn x = (tail $ show (foldl (\acc x -> acc + (digitToInt x)) 0 (foldr doEncrypt [] $ zip [0..] (show x)))) == "0"
    where
        doEncrypt (i,y) acc = if not(even i)
            then (head $ show(((uncurry (+) . (`divMod` 10) . (*2)) (digitToInt y)))) : acc
            else y : acc

Thanks a lot to @WillemVanOnsem for your pointers, without I probably wouldn't have solved this!