Using Parsec for simple arithmetic language
Where you used case you could have used pattern matching. This yields more idiomatic code in many cases.

isNumerical :: Term -&gt; Bool
isNumerical TmZero = True
isNumerical (TmSucc subterm) = isNumerical subterm
isNumerical (TmPred subterm) = isNumerical subterm
isNumerical _ = False

eval :: Term -&gt; Term
eval TmTrue  = TmTrue
eval TmFalse = TmFalse
eval TmZero  = TmZero
eval (TmIf term1 term2 term3) = evalIf (eval term1) term2 term3
eval (TmIsZero subterm) = evalIsZero $ eval subterm
eval (TmPred subterm) = evalPred $ eval subterm
eval (TmSucc subterm) = evalSucc $ eval subterm
eval TmError = TmError

evalIf :: Term -&gt; Term -&gt; Term -&gt; Term
evalIf TmTrue a  _ = eval a
evalIf TmFalse _ b = eval b
evalIf _ _ _  = TmError

evalIsZero :: Term -&gt; Term
evalIsZero TmZero = TmTrue
evalIsZero term
    | isNumerical term = TmFalse
    | otherwise        = TmError

evalPred :: Term -&gt; Term
evalPred TmZero = TmZero
evalPred t@(TmSucc subterm) = t
evalPred _ = TmError

evalSucc :: Term -&gt; Term
evalSucc term
    | isNumerical term = TmSucc term
    | otherwise        = TmError


Note the complex terms are farmed off to their own functions. This makes testing easier. Especially as the runtime gets more complex.

You asked about main's type. If you use forM_ you can define it as main :: IO ().

As for Applicative:

functionParser :: String -&gt; (Term -&gt; Term) -&gt; GenParser Char st Term
functionParser name funcTerm = funcTerm &lt;$&gt; (string (name ++ "(")
                                             *&gt; arithParser
                                             &lt;* char ')')
ifParser :: GenParser Char st Term
ifParser = TmIf &lt;$&gt; (string "if" *&gt; spaces *&gt; arithParser)
                &lt;*&gt; (spaces *&gt; string "then" *&gt; spaces *&gt; arithParser)
                &lt;*&gt; (spaces *&gt; string "else" *&gt; spaces *&gt; arithParser)


Also, the last 'try' should not be used. 'try' means to attempt a parse and do not consume the input if it failed. The last parse action is the end of the parse so there is no need to leave the input in the parser.