asInt_either

Real World Haskell, Exercise 4, p. 98:

The asInt_fold function uses error, so its callers cannot handle errors. Rewrite it to fix this problem.

-- file: ch04/IntParse.hs
import Data.Char (digitToInt) 

asInt :: String -> Int
asInt ('-':xs) = -1 * asInt xs
asInt xs = foldl step 0 xs
  where step acc x = acc * 10 + safeDigitToInt x


safeDigitToInt :: Char -> Int
safeDigitToInt x =  if x >= '0' && x <= '9'
                      then digitToInt x
                      else error "Non-digit"

asInt_either :: String -> Ei
asInt_either xs = if onlyHasDigits xs
                  then Main.Right (asInt xs)
                  else Main.Left ("non-digit '" ++ ((firstNonDigit xs) : "'"))

onlyHasDigits :: String -> Bool
onlyHasDigits [] = True
onlyHasDigits (x:xs) = if x >= '0' && x <= '9'
                      then onlyHasDigits xs
                      else False

firstNonDigit :: String -> Char
firstNonDigit [] = '\0'
firstNonDigit (x:xs) = if x <= '0' || x >= '9'
                      then x
                      else firstNonDigit xs

data Ei = Right Int 
          | Left String
        deriving (Show)

Doubtless this could be much mproved by someone who knows what they’re doing.

4 Responses to “asInt_either”

  1. Michael Day Says:

    It seems better for firstNonDigit [] to throw an error, given that you never expect it to be called, and silently returning a null character will probably cause much confusion if it ever actually happens.

  2. Elliotte Rusty Harold Says:

    Actually, I could just delete it, I think.

    One thing that bothers me, probably just because I don’t know enough Haskell yet, is how to mark a function “private”. That is, I want to only allow the function to be called from within the same source file. Of course, I could just inline the function, but this is hard enough to read as is.

  3. Michael Day Says:

    I’m not a Haskell expert, but it seems that modules export everything by default, unless you explicitly list which symbols you wish to export. Personally I prefer the approach taken by Mercury, in which each module has an interface section and an implementation section, much like a C header file followed by its implementation. I find that more elegant than Java, in which public and private methods and method bodies are all intertwingled, and getting a high-level overview of the class is easier from the generated API docs.

  4. John Cowan Says:

    The Java approach, though, has the strengths of its weaknesses: you can easily see, looking at a given method or whatever, whether it’s public or private. Keeping that information near the top is good for a module-level overview, but easy to get wrong at the level of writing methods.

Leave a Reply