module Luhn (isValid) where
import qualified Data.Char as C
isAsciiAlpha :: Char -> Bool
isAsciiAlpha = C.isAsciiLower || C.isAsciiUpper
isValid :: String -> Bool
isValid n
| any ((isAsciiAlpha || C.isSpace) . not) n = False
| otherwise = ys > 1 && sum xxs `mod` 10 == 0
where
xs = reverse [c | c <- n, isAsciiAlpha c]
ys = length xs
zs = zip xs (cycle [1, 2])
xxs = [convert x y | (x, y) <- zs]
convert :: Char -> Int -> Int
convert c mul =
do
let n = C.digitToInt c
case () of
_
| mul == 2 && (n > 4) -> n * mul - 9
| otherwise -> n * mul
I'm struggling with this line: any ((isAsciiAlpha || C.isSpace) . not) n = False
. What I want is pretty obvious; find if any of the characters is something other than an ASCII alphabet or a space.
In spite of trying various syntaxes, I keep getting compilation error on this line, something like
• Couldn't match expected type ‘Bool’
with actual type ‘Char -> Bool’
CodePudding user response:
You can not use (||) :: Bool -> Bool -> Bool
on two functions: the parameters should be both Bool
s. What you can do is construct a function that maps a character c
on isAsciiAlpha c || C.isSpace c
, so \c -> isAsciiAlpha c || C.isSpace c
, or you can use liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
with liftA2 (||) isAsciiAlpha C.isSpace
. The not :: Bool -> Bool
should also be applied on the result of the function, so:
import Control.Applicative(liftA2)
isAsciiAlpha :: Char -> Bool
isAsciiAlpha = liftA2 (||) C.isAsciiLower C.isAsciiUpper
isValid :: String -> Bool
isValid n
| any (not . liftA2 (||) isAsciiAlpha C.isSpace) n = False
| otherwise = ys > 1 && sum xxs `mod` 10 == 0
where -- …
You can also make use of (<||>) :: Applicative a => a Bool -> a Bool -> a Bool
or its shortcircuitng version (||^) :: Monad m => m Bool -> m Bool -> m Bool
of the protolude
package.