4
\$\begingroup\$

This code is a Parser that parses numbers according to R5RS.

  • #b1001 - binary
  • #o2127 - octal
  • #h02d - hexadecimal
  • #d1231 - decimal
  • 3923 - decimal

It is working at the moment, the only problem is the parseNumberBase. I am really new to haskell, but it does not look very good to me.

  • How could I improve it? (readability wise)
  • It would also be nice to see a more "idiomatic" approach

import Data.Char (digitToInt)
import Numeric (readInt, readOct, readHex)
import Data.Maybe (listToMaybe, fromJust)
parseNumber :: Parser LispVal
parseNumber = parseNumberBase 'd'
 <|> do char '#'
 base <- oneOf "bdoh"
 parseNumberBase base
-- | Parses a number at a specific base
parseNumberBase :: Char -> Parser LispVal
parseNumberBase 'b' =
 do digits <- many1 (oneOf "01")
 return $ (Number . fromJust . readBinary) digits
parseNumberBase 'o' =
 do digits <- many1 octDigit
 return $ Number (fst (readOct digits !! 0))
parseNumberBase 'd' =
 do digits <- many1 digit
 return $ (Number . read) digits
parseNumberBase 'h' =
 do digits <- many1 hexDigit
 return $ Number (fst (readHex digits !! 0))
parseNumberBase _ =
 error "Wrong number base"
readBinary :: String -> Maybe Integer
readBinary =
 fmap fst . listToMaybe . readInt 2 (`elem` "01") digitToInt
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Jan 30, 2016 at 23:17
\$\endgroup\$
1
  • \$\begingroup\$ You didn't include all code. What's Parser? \$\endgroup\$ Commented May 23, 2016 at 9:10

1 Answer 1

2
\$\begingroup\$
do
 digits <- someParser
 return $ someFunction digits

is just

someFunction <$> someParser

We can make parseNumberBase a lot shorter with that information:

-- | Parses a number at a specific base
parseNumberBase :: Char -> Parser LispVal
parseNumberBase 'b' = Number . fromJust . readBinary <$> many1 (oneOf "01")
parseNumberBase 'o' = Number . fst . head . readOct <$> many1 octDigit -- see below
parseNumberBase 'd' = Number . read <$> many1 digit
parseNumberBase 'h' = Number . fst . head . readHex <$> many1 hexDigit -- see below
parseNumberBase _ = error "Wrong number base"

readFunc digits !! 0 is head (readFunc digits), so we were able to get rid of !! too.

That being said, parseNumberBase has a lot of responsibility. Split it into multiple parsers, and it's suddenly a lot easier to grasp:

parseBinary :: Parser LispVal
parseBinary = Number . fromJust . readBinary <$> many1 (oneOf "01")
parseDecimal :: Parser LispVal
parseDecimal = Number . read <$> many1 digit
parseHexadecimal :: Parser LispVal
parseHexadecimal = Number . fst . head . readHex <$> many1 hexDigit
parseOct :: Parser LispVal
parseOct = Number . fst . head . readOct <$> many1 octDigit

We can now test all those functions in isolation. We can now write parseNumberBase as another parser:

parseNumber :: Parser LispVal
parseNumber = parseDecimal <|> char '#' *> parseNumberBase
parseNumberBase :: Parser LispVal
parseNumberBase = char 'h' *> parseHexadecimal 
 <|> char 'd' *> parseDecimal 
 <|> char 'o' *> parseOct
 <|> char 'b' *> parseBinary
answered Sep 26, 2017 at 16:35
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.