Skip to main content
added 8 characters in body
Source Link

First, I would convert all of the functions which might throw an error to return some kind of failable type, like Maybe Int or Either String Token. This includes str2tok, tokenize, and eval. This would also remove the need for the countTok function, since the program can just return an error value from eval instead. With the help of the Monad instances for these error types, this is a relatively simple change.

Next, I would use isDigit from Data.Char instead of c `elem` ['0'..'9'] because isDigit makes less comparisons in order to determine if it's a digit.

Lastly, I would change the Operator type to the function type Int -> Int -> Int. This will remove the need for applyOp and will consolidate all the places that would be necessary to change if you wanted to extend the program to accept more operators.

import Data.Char (isDigit)

data Token = TNum Int | TOp (Int -> Int -> Int)

main :: IO ()
main = do
    line <- getLine
    either putStrLn print $ do
        tokens <- tokenize line
        eval [] tokens

tokenize :: String -> Either String [Token]
tokenize = mapM str2tok . words

str2tok :: String -> Either String Token
str2tok tkn@tkn
    | (c:_)
   <- |tkn, isDigit c = Right $ TNum (read tkn)
    | otherwise = TOp <$> case tkn of
        "+" -> Right (+)
        "-" -> Right (-)
        "*" -> Right (*)
        "/" -> Right div
        _   -> Left $ "No such operator " ++ tkn

eval :: [Int] -> [Token] -> Either String Int
eval (s:_) []                   = Right s
eval stack (TNum t:ts)          = eval (t : stack) ts
eval (x:y:stacknoxy) (TOp t:ts) = eval (t y x : stacknoxy) ts
eval _ _                        = Left "Not a correct expression."

First, I would convert all of the functions which might throw an error to return some kind of failable type, like Maybe Int or Either String Token. This includes str2tok, tokenize, and eval. This would also remove the need for the countTok function, since the program can just return an error value from eval instead. With the help of the Monad instances for these error types, this is a relatively simple change.

Next, I would use isDigit from Data.Char instead of c `elem` ['0'..'9'] because isDigit makes less comparisons in order to determine if it's a digit.

Lastly, I would change the Operator type to the function type Int -> Int -> Int. This will remove the need for applyOp and will consolidate all the places that would be necessary to change if you wanted to extend the program to accept more operators.

import Data.Char (isDigit)

data Token = TNum Int | TOp (Int -> Int -> Int)

main :: IO ()
main = do
    line <- getLine
    either putStrLn print $ do
        tokens <- tokenize line
        eval [] tokens

tokenize :: String -> Either String [Token]
tokenize = mapM str2tok . words

str2tok :: String -> Either String Token
str2tok tkn@(c:_)
    | isDigit c = Right $ TNum (read tkn)
    | otherwise = TOp <$> case tkn of
        "+" -> Right (+)
        "-" -> Right (-)
        "*" -> Right (*)
        "/" -> Right div
        _   -> Left $ "No such operator " ++ tkn

eval :: [Int] -> [Token] -> Either String Int
eval (s:_) []                   = Right s
eval stack (TNum t:ts)          = eval (t : stack) ts
eval (x:y:stacknoxy) (TOp t:ts) = eval (t y x : stacknoxy) ts
eval _ _                        = Left "Not a correct expression."

First, I would convert all of the functions which might throw an error to return some kind of failable type, like Maybe Int or Either String Token. This includes str2tok, tokenize, and eval. This would also remove the need for the countTok function, since the program can just return an error value from eval instead. With the help of the Monad instances for these error types, this is a relatively simple change.

Next, I would use isDigit from Data.Char instead of c `elem` ['0'..'9'] because isDigit makes less comparisons in order to determine if it's a digit.

Lastly, I would change the Operator type to the function type Int -> Int -> Int. This will remove the need for applyOp and will consolidate all the places that would be necessary to change if you wanted to extend the program to accept more operators.

import Data.Char (isDigit)

data Token = TNum Int | TOp (Int -> Int -> Int)

main :: IO ()
main = do
    line <- getLine
    either putStrLn print $ do
        tokens <- tokenize line
        eval [] tokens

tokenize :: String -> Either String [Token]
tokenize = mapM str2tok . words

str2tok :: String -> Either String Token
str2tok tkn
    | (c:_) <- tkn, isDigit c = Right $ TNum (read tkn)
    | otherwise = TOp <$> case tkn of
        "+" -> Right (+)
        "-" -> Right (-)
        "*" -> Right (*)
        "/" -> Right div
        _   -> Left $ "No such operator " ++ tkn

eval :: [Int] -> [Token] -> Either String Int
eval (s:_) []                   = Right s
eval stack (TNum t:ts)          = eval (t : stack) ts
eval (x:y:stacknoxy) (TOp t:ts) = eval (t y x : stacknoxy) ts
eval _ _                        = Left "Not a correct expression."
added 6 characters in body
Source Link

First, I would convert all of the functions which might throw an error to return some kind of failable type, like Maybe Int or Either String Token. This includes str2tok, tokenize, and eval. This would also remove the need for the countTok function, since the program can just return an error value from eval instead. With the help of the Monad instances for these error types, this is a relatively simple change.

import Data.Char (isDigit)

data Token = TNum Int | TOp (Int -> Int -> Int)

main :: IO ()
main = do
    line <- getLine
    caseeither tokenizeputStrLn lineprint of
$ do
       Left errtokens <-> putStrLntokenize errline
        Right tokens -> case eval [] tokens of
            Nothing -> putStrLn "Not a correct expression."
            Just val -> print val

tokenize :: String -> Either String [Token]
tokenize = mapM str2tok . words

str2tok :: String -> Either String Token
str2tok tkn@(c:_)
    | isDigit c = Right $ TNum (read tkn :: Int)
    | otherwise = TOp <$> case tkn of
        "+" -> Right (+)
        "-" -> Right (-)
        "*" -> Right (*)
        "/" -> Right div
        _   -> Left $ "No such operator " ++ tkn

eval :: [Int] -> [Token] -> MaybeEither String Int
eval (s:_) []                   = JustRight s
eval stack (TNum t:ts)          = eval (t : stack) ts
eval (x:y:stacknoxy) (TOp t:ts) = eval (t y x : stacknoxy) ts
eval _ _                        = NothingLeft "Not a correct expression."

First, I would convert all of the functions which might throw an error to return some kind of failable type, like Maybe Int or Either String Token. This includes str2tok, tokenize, and eval. This would also remove the need for the countTok function, since the program can just return an error value from eval instead.

import Data.Char (isDigit)

data Token = TNum Int | TOp (Int -> Int -> Int)

main :: IO ()
main = do
    line <- getLine
    case tokenize line of
        Left err -> putStrLn err
        Right tokens -> case eval [] tokens of
            Nothing -> putStrLn "Not a correct expression."
            Just val -> print val

tokenize :: String -> Either String [Token]
tokenize = mapM str2tok . words

str2tok :: String -> Either String Token
str2tok tkn@(c:_)
    | isDigit c = Right $ TNum (read tkn :: Int)
    | otherwise = TOp <$> case tkn of
        "+" -> Right (+)
        "-" -> Right (-)
        "*" -> Right (*)
        "/" -> Right div
        _   -> Left $ "No such operator " ++ tkn

eval :: [Int] -> [Token] -> Maybe Int
eval (s:_) []                   = Just s
eval stack (TNum t:ts)          = eval (t : stack) ts
eval (x:y:stacknoxy) (TOp t:ts) = eval (t y x : stacknoxy) ts
eval _ _                        = Nothing

First, I would convert all of the functions which might throw an error to return some kind of failable type, like Maybe Int or Either String Token. This includes str2tok, tokenize, and eval. This would also remove the need for the countTok function, since the program can just return an error value from eval instead. With the help of the Monad instances for these error types, this is a relatively simple change.

import Data.Char (isDigit)

data Token = TNum Int | TOp (Int -> Int -> Int)

main :: IO ()
main = do
    line <- getLine
    either putStrLn print $ do
        tokens <- tokenize line
        eval [] tokens

tokenize :: String -> Either String [Token]
tokenize = mapM str2tok . words

str2tok :: String -> Either String Token
str2tok tkn@(c:_)
    | isDigit c = Right $ TNum (read tkn)
    | otherwise = TOp <$> case tkn of
        "+" -> Right (+)
        "-" -> Right (-)
        "*" -> Right (*)
        "/" -> Right div
        _   -> Left $ "No such operator " ++ tkn

eval :: [Int] -> [Token] -> Either String Int
eval (s:_) []                   = Right s
eval stack (TNum t:ts)          = eval (t : stack) ts
eval (x:y:stacknoxy) (TOp t:ts) = eval (t y x : stacknoxy) ts
eval _ _                        = Left "Not a correct expression."
Source Link

First, I would convert all of the functions which might throw an error to return some kind of failable type, like Maybe Int or Either String Token. This includes str2tok, tokenize, and eval. This would also remove the need for the countTok function, since the program can just return an error value from eval instead.

Next, I would use isDigit from Data.Char instead of c `elem` ['0'..'9'] because isDigit makes less comparisons in order to determine if it's a digit.

Lastly, I would change the Operator type to the function type Int -> Int -> Int. This will remove the need for applyOp and will consolidate all the places that would be necessary to change if you wanted to extend the program to accept more operators.

import Data.Char (isDigit)

data Token = TNum Int | TOp (Int -> Int -> Int)

main :: IO ()
main = do
    line <- getLine
    case tokenize line of
        Left err -> putStrLn err
        Right tokens -> case eval [] tokens of
            Nothing -> putStrLn "Not a correct expression."
            Just val -> print val

tokenize :: String -> Either String [Token]
tokenize = mapM str2tok . words

str2tok :: String -> Either String Token
str2tok tkn@(c:_)
    | isDigit c = Right $ TNum (read tkn :: Int)
    | otherwise = TOp <$> case tkn of
        "+" -> Right (+)
        "-" -> Right (-)
        "*" -> Right (*)
        "/" -> Right div
        _   -> Left $ "No such operator " ++ tkn

eval :: [Int] -> [Token] -> Maybe Int
eval (s:_) []                   = Just s
eval stack (TNum t:ts)          = eval (t : stack) ts
eval (x:y:stacknoxy) (TOp t:ts) = eval (t y x : stacknoxy) ts
eval _ _                        = Nothing