5
\$\begingroup\$

I have written a safe read function for haskell (for education!). I think this is fairly idiomatic haskell code, but I'd like to know for sure. If you see anything else that could be improved, please let me know in your answer.

module ReadNum (readNum) where

main = print $ readNum "87k7"

readNum :: String -> Maybe Int
readNum xs = readNum' $ reverse xs

readNum' :: String -> Maybe Int
readNum' (x:xs) = (\y z -> y + 10 * z) <$> readChr x <*> readNum' xs
readNum' [] = Just 0

chrToNum :: [(Char, Int)]
chrToNum = [('0', 0), ('1', 1),
            ('2', 2), ('3', 3),
            ('4', 4), ('5', 5),
            ('6', 6), ('7', 7),
            ('8', 8), ('9', 9)]

readChr :: Char -> Maybe Int
readChr c = lookup c chrToNum
\$\endgroup\$
1
\$\begingroup\$

The change I'd make, respecting your overall design, is to refactor chrToNum as this:

chrToNum :: [(Char, Int)]
chrToNum = zip "0123456789" [0..]

Apart from that, if we are willing to slightly alter the design, I would separate the string validation from the computation of the Int represented. The string validation can be done using mapM :: (a -> m b) -> [a] -> m [b] (this way we avoid explicitly writing the recursion).

validateString :: String -> Maybe [Int]
validateString = mapM readChr

Then, you can define a function computing the Int represented by a list of digits (I use foldl' which requires you to add import Data.List but you can equally well use reverse + foldr as you originally did)

represents :: [Int] -> Int
represents = foldl' (\ ih z -> 10 * ih + z) 0

Finally, putting it all together we get:

readNum :: String -> Maybe Int
readNum = fmap represents . validateString
\$\endgroup\$
  • 1
    \$\begingroup\$ Cool, nice answer! So, mapM fails if any of the computations fail? (for the Maybe monad at least) \$\endgroup\$ – J Atkin Mar 27 '16 at 1:33
  • \$\begingroup\$ Yes. mapM has a structure similar to the one of your readNum': mapM f [] = pure [] and mapM f (x : xs) = (:) <$> f x <*> mapM f xs. \$\endgroup\$ – gallais Mar 27 '16 at 12:34
  • \$\begingroup\$ As a minor improvement: [0..] -> [0..9] to be more explicit. \$\endgroup\$ – Caridorc Mar 27 '16 at 18:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.