I recently went through this [Haskell tutorial](http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours). 
I'd be interested in any thoughts or comments at all, in terms of improving the structure, order, haskell conventions, or that long, kind of ugly eval function. 

    {-# LANGUAGE ExistentialQuantification #-}

    module SchemeParser (LispVal (..), LispError (..), readExpr, eval, runIOThrows, primitiveBindings, liftThrows) where

    import Control.Monad
    import Control.Monad.Error
    import Data.Array
    import Data.Char
    import Data.Complex
    import Data.IORef
    import Data.Maybe
    import Data.Ratio
    import Numeric
    import System.Environment
    import System.IO
    import Text.ParserCombinators.Parsec hiding (spaces1)

    data LispVal = Atom String
                    | List [LispVal]
                    | DottedList [LispVal] LispVal
                    | Vector (Array Int LispVal)
                    | Number Integer
                    | Float Double
                    | Complex  (Complex Double)
                    | Rational Rational
                    | String String
                    | Bool Bool
                    | Char Char
                    | PrimitiveFunc ([LispVal] -> ThrowsError LispVal)
                    | Func { params :: [String], vararg :: Maybe String,
                        body :: [LispVal], closure :: Env }
                    | IOFunc ([LispVal] -> IOThrowsError LispVal)
                    | Port Handle

    instance Show LispVal where show = showVal
    instance Eq LispVal where a == b = a `eq` b
    eq :: LispVal -> LispVal -> Bool
    eq (Atom a) (Atom b) = a == b
    eq (List a) (List b) = a == b
    eq (DottedList a b)(DottedList c d) = a == c && b == d
    eq (Vector a) (Vector b) = a==b
    eq (Number a) (Number b) = a==b
    eq (Float a) (Float b) = a==b
    eq (Complex a) (Complex b) = a==b
    eq (Rational a) (Rational b) = a==b
    eq (String a) (String b) = a==b
    eq (Bool a) (Bool b) = a==b
    eq (Char a) (Char b) = a==b
    eq _ _ = False

    data LispError = NumArgs Integer [LispVal]
                   | TypeMismatch String LispVal
                   | Parser ParseError
                   | BadSpecialForm String LispVal
                   | NotFunction String String
                   | UnboundVar String String
                   | Default String

    instance Show LispError where show = showError
    instance Error LispError where
         noMsg = Default "An error has occurred"
         strMsg = Default
    instance Eq LispError where a == b = a `eqError` b

    eqError (NumArgs a b) (NumArgs c d) = (a == c) && (b == d)
    eqError (TypeMismatch a b) (TypeMismatch c d) = (a == c) && (b == d)
    eqError (BadSpecialForm a b) (BadSpecialForm c d) = (a == c) && (b == d)
    eqError (NotFunction a b) (NotFunction c d) = (a == c) && (b == d)
    eqError (UnboundVar a b) (UnboundVar c d) = (a == c) && (b == d)
    eqError (Default a) (Default b) = a == b
    eqError a b  = False

    type ThrowsError = Either LispError

    data Unpacker = forall a. Eq a => AnyUnpacker (LispVal -> ThrowsError a)

    type Env = IORef [(String, IORef LispVal)]

    type IOThrowsError = ErrorT LispError IO

    nullEnv :: IO Env
    nullEnv = newIORef []

    liftThrows :: ThrowsError a -> IOThrowsError a
    liftThrows (Left err) = throwError err
    liftThrows (Right val) = return val

    runIOThrows :: IOThrowsError String -> IO String
    runIOThrows action = liftM extractValue (runErrorT (trapError action))

    isBound :: Env -> String -> IO Bool
    isBound envRef var = liftM (isJust . lookup var) (readIORef envRef)

    getVar :: Env -> String -> IOThrowsError LispVal
    getVar envRef var  =  do env <- liftIO $ readIORef envRef
                             maybe (throwError $ UnboundVar "Getting an unbound variable" var)
                                   (liftIO . readIORef)
                                   (lookup var env)

    setVar :: Env -> String -> LispVal -> IOThrowsError LispVal
    setVar envRef var value = do env <- liftIO $ readIORef envRef
                                 maybe (throwError $ UnboundVar "Setting an unbound variable" var)
                                       (liftIO . flip writeIORef value)
                                       (lookup var env)
                                 return value

    defineVar :: Env -> String -> LispVal -> IOThrowsError LispVal
    defineVar envRef var value = do
         alreadyDefined <- liftIO $ isBound envRef var
         if alreadyDefined
            then setVar envRef var value >> return value
            else liftIO $ do
                 valueRef <- newIORef value
                 env <- readIORef envRef
                 writeIORef envRef ((var, valueRef) : env)
                 return value
    bindVars :: Env -> [(String, LispVal)] -> IO Env
    bindVars envRef bindings = readIORef envRef >>= extendEnv bindings >>= newIORef
         where extendEnv bindings env = liftM (++ env) (mapM addBinding bindings)
               addBinding (var, value) = do ref <- newIORef value
                                            return (var, ref)


    flushStr :: String -> IO ()
    flushStr str = putStr str >> hFlush stdout

    readPrompt :: String -> IO String
    readPrompt prompt = flushStr prompt >> getLine

    evalString :: Env -> String -> IO String
    evalString env expr = runIOThrows $ liftM show $ liftThrows (readExpr expr) >>= eval env

    evalAndPrint :: Env -> String -> IO ()
    evalAndPrint env expr =  evalString env expr >>= putStrLn

    until_ :: Monad m => (a -> Bool) -> m a -> (a -> m ()) -> m ()
    until_ pred prompt action = do
       result <- prompt
       unless (pred result) $ action result >> until_ pred prompt action

    primitiveBindings :: IO Env
    primitiveBindings = nullEnv >>= flip bindVars (map (makeFunc IOFunc) ioPrimitives ++
                                                   map (makeFunc PrimitiveFunc) primitives)
         where makeFunc constructor (var, func) = (var, constructor func)

    runOne :: [String] -> IO ()
    runOne args = do
        env <- primitiveBindings >>= flip bindVars [("args", List $ map String $ drop 1 args)]
        runIOThrows (liftM show $ eval env (List [Atom "load", String (head args)]))
            >>= hPutStrLn stderr

    runRepl :: IO ()
    runRepl = primitiveBindings >>= until_ (== "quit") (readPrompt "Lisp>>> ") . evalAndPrint

    main :: IO ()
    main = do args <- getArgs
              if null args then runRepl else runOne args

    extractValue :: ThrowsError a -> a
    extractValue (Right val) = val

    trapError action = catchError action (return . show)

    readOrThrow :: Parser a -> String -> ThrowsError a
    readOrThrow parser input = case parse parser "lisp" input of
        Left err  -> throwError $ Parser err
        Right val -> return val

    readExpr = readOrThrow parseExpr
    readExprList = readOrThrow (endBy parseExpr spaces)

    showVal :: LispVal -> String
    showVal (String contents) = "\"" ++ contents ++ "\""
    showVal (Atom name) = name
    showVal (Number contents) = show contents
    showVal (Complex c) = show c
    showVal (Float f) = show f
    showVal (Rational r) = show r
    showVal (Bool True) = "#t"
    showVal (Bool False) = "#f"
    showVal (List contents) = "(" ++ unwordsList contents ++ ")"
    showVal (Vector arr) = "(" ++ unwordsList (elems arr) ++ ")"
    showVal (DottedList head tail) = "(" ++ unwordsList head ++ "." ++ showVal tail ++ ")"
    showVal (PrimitiveFunc _) = "<primitive>"
    showVal (Func {params = args, vararg = varargs, body = body, closure = env}) =
       "(lambda (" ++ unwords (map show args) ++
          (case varargs of
             Nothing -> ""
             Just arg -> " . " ++ arg) ++ ") ...)"
    showVal (Port _)   = "<IO port>"
    showVal (IOFunc _) = "<IO primitive>"

    unwordsList :: [LispVal] -> String
    unwordsList = unwords . map showVal

    showError :: LispError -> String
    showError (UnboundVar message varname)  = message ++ ": " ++ varname
    showError (BadSpecialForm message form) = message ++ ": " ++ show form
    showError (NotFunction message func)    = message ++ ": " ++ show func
    showError (NumArgs expected found)      = "Expected " ++ show expected
                                           ++ " args; found values " ++ unwordsList found
    showError (TypeMismatch expected found) = "Invalid type: expected " ++ expected
                                           ++ ", found " ++ show found
    showError (Parser parseErr)             = "Parse error at " ++ show parseErr
    showError (Default message)             = "Error: " ++ message

    parseExpr :: Parser LispVal
    parseExpr =  try parseBool
              <|> parseString
              <|> parseVector
              <|> parseAtom
              <|> parseChar
              <|> try parseComplexNumber
              <|> try parseFloat
              <|> try parseRationalNumber
              <|> parseNumber
              <|> parseQuoted
              <|> parseQuasiQuoted
              <|> parseUnQuote
              <|> parseAllTheLists

    makeFunc varargs env params body = return $ Func (map showVal params) varargs body env
    makeNormalFunc = makeFunc Nothing
    makeVarArgs = makeFunc . Just . showVal

    eval :: Env -> LispVal -> IOThrowsError LispVal
    eval env val@(String _) = return val
    eval env val@(Number _) = return val
    eval env val@(Bool _) = return val
    eval env (Atom id) = getVar env id
    eval env (List [Atom "load", String filename]) =
         load filename >>= liftM last . mapM (eval env)
    eval env (List [Atom "quote", val]) = return val
    eval env (List [Atom "if", pred, conseq, alt ]) = do
      result <- eval env pred
      case result of
        Bool False -> eval env alt
        Bool True -> eval env conseq
        otherwise -> throwError $ BadSpecialForm "Predicate must be boolean in" otherwise
    eval env (List (Atom "cond" : (h@(List [test, expr]) : clauses))) = do
      result <- eval env test
      case result of
        Bool True -> eval env expr
        Bool False -> eval env (List (Atom "cond" : clauses))
        pred -> throwError $ TypeMismatch "boolean" pred
    eval env (l@(List (Atom "cond": []))) = throwError $ BadSpecialForm "One of the conditions must be true" l
    eval env (List (Atom "cond": a)) = throwError $ TypeMismatch "list" (head a)
    eval env form@(List (Atom "case" : key : clauses)) =
      if null clauses
      then throwError $ BadSpecialForm "no true clause in case expression: " form
      else case head clauses of
        List (Atom "else" : exprs) -> liftM last (mapM (eval env) exprs)
        List (List datums : exprs) -> do
          result <- eval env key
          equality <-  liftThrows (mapM (\x -> eqv [result, x]) datums)
          if Bool True `elem` equality
            then liftM last (mapM (eval env) exprs)
            else eval env $ List (Atom "case" : key : tail clauses)
        _  -> throwError $ BadSpecialForm "ill-formed case expression: " form
    eval env (List [Atom "set!", Atom var, form]) = eval env form >>= setVar env var
    eval env (List [Atom "define", Atom var, form]) = eval env form >>= defineVar env var
    eval env (List (Atom "define" : List (Atom var : params) : body)) =
      makeNormalFunc env params body >>= defineVar env var
    eval env (List (Atom "define" : DottedList (Atom var : params) varargs : body)) =
       makeVarArgs varargs env params body >>= defineVar env var
    eval env (List (Atom "lambda" : List params : body)) =
       makeNormalFunc env params body
    eval env (List (Atom "lambda" : DottedList params varargs : body)) =
       makeVarArgs varargs env params body
    eval env (List (Atom "lambda" : varargs@(Atom _) : body)) =
       makeVarArgs varargs env [] body
    eval env (List (function : args)) = do
       func <- eval env function
       argVals <- mapM (eval env) args
       apply func argVals
    eval env (List elems) = return $ List elems
    eval env badForm = throwError $ BadSpecialForm "Unrecognized special form" badForm

    apply :: LispVal -> [LispVal] -> IOThrowsError LispVal
    apply (PrimitiveFunc func) args = liftThrows $ func args
    apply (Func params varargs body closure) args =
          if num params /= num args && isNothing varargs
             then throwError $ NumArgs (num params) args
             else liftIO (bindVars closure $ zip params args) >>= bindVarArgs varargs >>= evalBody
          where remainingArgs = drop (length params) args
                num = toInteger . length
                evalBody env = liftM last $ mapM (eval env) body
                bindVarArgs arg env = case arg of
                    Just argName -> liftIO $ bindVars env [(argName, List remainingArgs)]
                    Nothing -> return env
    apply (IOFunc func) args = func args
    apply a bs = return $ List (a:bs)

    applyProc :: [LispVal] -> IOThrowsError LispVal
    applyProc [func, List args] = apply func args
    applyProc (func : args)     = apply func args

    primitives :: [(String, [LispVal] -> ThrowsError LispVal)]
    primitives = [("+", numericBinop (+)),
                  ("-", numericBinop (-)),
                  ("*", numericBinop (*)),
                  ("/", numericBinop div),
                  ("mod", numericBinop mod),
                  ("quotient", numericBinop quot),
                  ("remainder", numericBinop rem),
                  ("symbol?", unaryOp symbolp),
                  ("string?" , unaryOp stringp),
                  ("number?" , unaryOp numberp),
                  ("bool?", unaryOp boolp),
                  ("list?" , unaryOp listp),
                  ("symbol->string", unaryOp symbol2string),
                  ("string->symbol", unaryOp string2symbol),
                  ("=", numBoolBinop (==)),
                  ("<", numBoolBinop (<)),
                  (">", numBoolBinop (>)),
                  ("/=", numBoolBinop (/=)),
                  (">=", numBoolBinop (>=)),
                  ("<=", numBoolBinop (<=)),
                  ("&&", boolBoolBinop (&&)),
                  ("||", boolBoolBinop (||)),
                  ("string=?", strBoolBinop (==)),
                  ("string<?", strBoolBinop (<)),
                  ("string>?", strBoolBinop (>)),
                  ("string<=?", strBoolBinop (<=)),
                  ("string>=?", strBoolBinop (>=)),
                  ("car", car),
                  ("cdr", cdr),
                  ("cons", cons),
                  ("eq?", eqv),
                  ("eqv?", eqv),
                  ("equal?", equal)
                  ]

    ioPrimitives :: [(String, [LispVal] -> IOThrowsError LispVal)]
    ioPrimitives = [("apply", applyProc),
                    ("open-input-file", makePort ReadMode),
                    ("open-output-file", makePort WriteMode),
                    ("close-input-port", closePort),
                    ("close-output-port", closePort),
                    ("read", readProc),
                    ("write", writeProc),
                    ("read-contents", readContents),
                    ("read-all", readAll)]

    makePort :: IOMode -> [LispVal] -> IOThrowsError LispVal
    makePort mode [String filename] = liftM Port $ liftIO $ openFile filename mode

    closePort :: [LispVal] -> IOThrowsError LispVal
    closePort [Port port] = liftIO $ hClose port >> return (Bool True)
    closePort _           = return $ Bool False

    readProc :: [LispVal] -> IOThrowsError LispVal
    readProc []          = readProc [Port stdin]
    readProc [Port port] = liftIO (hGetLine port) >>= liftThrows . readExpr

    writeProc :: [LispVal] -> IOThrowsError LispVal
    writeProc [obj]            = writeProc [obj, Port stdout]
    writeProc [obj, Port port] = liftIO $ hPrint port obj >> return (Bool True)

    readContents :: [LispVal] -> IOThrowsError LispVal
    readContents [String filename] = liftM String $ liftIO $ readFile filename

    load :: String -> IOThrowsError [LispVal]
    load filename = liftIO (readFile filename) >>= liftThrows . readExprList

    readAll :: [LispVal] -> IOThrowsError LispVal
    readAll [String filename] = liftM List $ load filename

    listOp :: ([LispVal] -> ThrowsError LispVal) ->[LispVal] -> ThrowsError LispVal
    listOp op = op

    numericBinop :: (Integer -> Integer -> Integer) -> [LispVal] -> ThrowsError LispVal
    numericBinop op           []  = throwError $ NumArgs 2 []
    numericBinop op singleVal@[_] = throwError $ NumArgs 2 singleVal
    numericBinop op params        = liftM (Number . foldl1 op) (mapM unpackNum params)

    unpackNum :: LispVal -> ThrowsError Integer
    unpackNum (Number n) = return n
    unpackNum (String n) = let parsed = reads n in
                               if null parsed
                                 then throwError $ TypeMismatch "number" $ String n
                                 else return $ fst $ head parsed
    unpackNum (List [n]) = unpackNum n
    unpackNum notNum     = throwError $ TypeMismatch "number" notNum


    unpackStr :: LispVal -> ThrowsError String
    unpackStr (String s) = return s
    unpackStr (Number s) = return $ show s
    unpackStr (Bool s)   = return $ show s
    unpackStr notString  = throwError $ TypeMismatch "string" notString

    unpackBool :: LispVal -> ThrowsError Bool
    unpackBool (Bool b) = return b
    unpackBool notBool  = throwError $ TypeMismatch "boolean" notBool

    parseVector :: Parser LispVal
    parseVector = do string "#("
                     elems <- sepBy parseExpr spaces1
                     char ')'
                     return $ Vector (listArray (0, length elems -1) elems)

    unaryOp :: (LispVal -> ThrowsError LispVal) -> [LispVal] -> ThrowsError LispVal
    unaryOp f [v] = f v

    symbolp, numberp, stringp, boolp, listp, string2symbol, symbol2string :: LispVal -> ThrowsError LispVal
    symbolp (Atom _) = return $ Bool True
    symbolp _ = return $ Bool False

    numberp (Number _) = return $ Bool True
    numberp _          = return $ Bool False

    stringp (String _) = return $ Bool True
    stringp _          = return $ Bool False

    boolp   (Bool _)   = return  $ Bool True
    boolp   _          = return $ Bool False

    listp   (List _)   = return $ Bool True
    listp   (DottedList _ _) = return $ Bool True
    listp   _          = return $ Bool False

    string2symbol (String x) = return $ Atom x
    string2symbol s = throwError $ TypeMismatch "string" s

    symbol2string (Atom x) = return $ String x
    symbol2string s = throwError $ TypeMismatch "symbol" s

    boolBinop :: (LispVal -> ThrowsError a) -> (a -> a -> Bool) -> [LispVal] -> ThrowsError LispVal
    boolBinop unpacker op args = if length args /= 2
                                 then throwError $ NumArgs 2 args
                                 else do left <- unpacker $ head args
                                         right <- unpacker $ args !! 1
                                         return $ Bool $ left `op` right

    numBoolBinop  = boolBinop unpackNum
    strBoolBinop  = boolBinop unpackStr
    boolBoolBinop = boolBinop unpackBool

    car :: [LispVal] -> ThrowsError LispVal
    car [List (x : xs)]         = return x
    car [DottedList (x : xs) _] = return x
    car [badArg]                = throwError $ TypeMismatch "pair" badArg
    car badArgList              = throwError $ NumArgs 1 badArgList

    cdr :: [LispVal] -> ThrowsError LispVal
    cdr [List (x : xs)]         = return $ List xs
    cdr [DottedList [_] x]      = return x
    cdr [DottedList (_ : xs) x] = return $ DottedList xs x
    cdr [badArg]                = throwError $ TypeMismatch "pair" badArg
    cdr badArgList              = throwError $ NumArgs 1 badArgList

    cons :: [LispVal] -> ThrowsError LispVal
    cons [x1, List []] = return $ List [x1]
    cons [x, List xs] = return $ List $ x : xs
    cons [x, DottedList xs xlast] = return $ DottedList (x : xs) xlast
    cons [x1, x2] = return $ DottedList [x1] x2
    cons badArgList = throwError $ NumArgs 2 badArgList

    eqv :: [LispVal] -> ThrowsError LispVal
    eqv [Bool arg1, Bool arg2]             = return $ Bool $ arg1 == arg2
    eqv [Number arg1,Number arg2]         = return $ Bool $ arg1 == arg2
    eqv [String arg1, String arg2]         = return $ Bool $ arg1 == arg2
    eqv [Atom arg1, Atom arg2]             = return $ Bool $ arg1 == arg2
    eqv [DottedList xs x, DottedList ys y] = eqv [List $ xs ++ [x], List $ ys ++ [y]]
    eqv [l1@(List arg1), l2@(List arg2)]       = eqvList eqv [l1, l2]
    eqv [_, _]                                 = return $ Bool False
    eqv badArgList                             = throwError $ NumArgs 2 badArgList

    eqvList :: ([LispVal] -> ThrowsError LispVal) -> [LispVal] -> ThrowsError LispVal
    eqvList eqvFunc [List arg1, List arg2] = return $ Bool $ (length arg1 == length arg2) &&
                                                      all eqvPair (zip arg1 arg2)
       where eqvPair (x1, x2) = case eqvFunc [x1, x2] of
                                     Left err -> False
                                     Right (Bool val) -> val

    unpackEquals :: LispVal -> LispVal -> Unpacker -> ThrowsError Bool
    unpackEquals arg1 arg2 (AnyUnpacker unpacker) =
                 do unpacked1 <- unpacker arg1
                    unpacked2 <- unpacker arg2
                    return $ unpacked1 == unpacked2
            `catchError` const (return False)

    equal :: [LispVal] -> ThrowsError LispVal
    equal [l1@(List arg1), l2@(List arg2)] = eqvList equal [l1, l2]
    equal [DottedList xs x, DottedList ys y] = equal [List $ xs ++ [x], List $ ys ++ [y]]
    equal [arg1, arg2] = do
       primitiveEquals <- liftM or $ mapM (unpackEquals arg1 arg2)
                          [AnyUnpacker unpackNum, AnyUnpacker unpackStr, AnyUnpacker unpackBool]
       eqvEquals <- eqv [arg1, arg2]
       return $ Bool (primitiveEquals || let (Bool x) = eqvEquals in x)
    equal badArgList = throwError $ NumArgs 2 badArgList



    parseAllTheLists ::Parser LispVal
    parseAllTheLists = do char '(' >> spaces
                          head <- sepEndBy parseExpr spaces1
                          do  char '.' >> spaces1
                              tail <- parseExpr
                              spaces >> char ')'
                              return $ DottedList head tail
                              <|> (spaces >> char ')' >> return (List head))

    parseQuoted :: Parser LispVal
    parseQuoted = do
            char '\''
            x <- parseExpr
            return $ List [Atom "quote", x]

    parseQuasiQuoted :: Parser LispVal
    parseQuasiQuoted = do
       char '`'
       x <- parseExpr
       return $ List [Atom "quasiquote", x]

    parseUnQuote :: Parser LispVal
    parseUnQuote = do
       char ','
       x <- parseExpr
       return $ List [Atom "unquote", x]

    parseComplexNumber :: Parser LispVal
    parseComplexNumber = do realPart <- fmap toDouble $ try parseFloat <|> readPlainNumber
                            sign <- char '+' <|> char '-'
                            imaginaryPart <- fmap toDouble $ try parseFloat <|> readPlainNumber
                            let signedImaginaryPart = case sign of
                                                        '+' -> imaginaryPart
                                                        '-' -> negate imaginaryPart
                            char 'i'
                            return $ Complex (realPart :+ signedImaginaryPart)
                                where toDouble (Float x) = x
                                      toDouble (Number x) = fromInteger x :: Double

    parseRationalNumber :: Parser LispVal
    parseRationalNumber = do numerator <- many digit
                             char '/'
                             denominator <- many digit
                             return $ Rational (read (numerator ++ "%" ++ denominator) :: Rational)

    parseFloat :: Parser LispVal
    parseFloat = do whole <- many1 digit
                    char '.'
                    decimal <- many1 digit
                    return $ Float (read (whole ++ "." ++ decimal))

    parseAtom :: Parser LispVal
    parseAtom = do first <- letter <|> symbol
                   rest <- many (letter <|> digit <|> symbol)
                   let atom = first:rest
                   return $ Atom atom

    parseBool :: Parser LispVal
    parseBool = do
        char '#'
        (char 't' >> return (Bool True)) <|> (char 'f' >> return (Bool False))

    symbol :: Parser Char
    symbol = oneOf "!$%&|*+-/:<=>?@^_~"

    spaces1 :: Parser ()
    spaces1 = skipMany1 space

    parseChar :: Parser LispVal
    parseChar = do string "#\\"
                   c <- many1 letter
                   return $ case map toLower c of
                       "newline" -> Char '\n'
                       "space" -> Char ' '
                       [x] -> Char x

    escapedChar :: Parser Char
    escapedChar = char '\\' >> oneOf "\"nrt\\" >>= \c ->
                                return $ case c of
                                        '\\' -> '\\'
                                        'n' -> '\n'
                                        'r' -> '\r'
                                        't' -> '\t'

    parseString :: Parser LispVal
    parseString = do  char '"'
                      x <- many (noneOf "\"" <|> escapedChar)
                      char '"'
                      return $ String x

    parseNumber :: Parser LispVal
    parseNumber = readPlainNumber <|> parseRadixNumber

    readPlainNumber:: Parser LispVal
    readPlainNumber = do
                        d <- many1 digit
                        return $ Number . read $ d

    parseRadixNumber :: Parser LispVal
    parseRadixNumber = char '#' >>
                        ((char 'd' >> readPlainNumber)
                         <|> (char 'b' >> readBinaryNumber)
                         <|> (char 'o' >> readOctalNumber)
                         <|> (char 'x' >> readHexNumber))

    readBinaryNumber = readNumberInBase "01" 2
    readOctalNumber = readNumberInBase "01234567" 8
    readHexNumber = readNumberInBase "0123456789abcdefABCEDF" 16

    readNumberInBase :: String -> Integer -> Parser LispVal
    readNumberInBase digits base = do
                        d <- many (oneOf digits)
                        return $ Number $ toDecimal base d

    toDecimal :: Integer -> String -> Integer
    toDecimal base s = foldl1 ((+) . (* base)) $ map toNumber s
                        where toNumber  = toInteger . digitToInt