The second part of your case statement should not try to retest, because you already know that this is not an Error that would correspond to the first. (Skip /= Error err .)
eval e1 = v1 trying to redo the eval you did at the beginning. You do not need to do this.
Here I think you intended to do:
eval :: Exp -> Error Val eval (App e1 e2) = case eval e1 of Error _ -> Error "Not an application" S v1 -> case eval e2 of -- nested case statement Error _ -> Error "Not an application" S v2 -> appVals v1 v2 -- match the S away
But all this seems a little ugly, so let me digress from Gabriel Gonzalez and make an application from Error .
instance Functor Error where fmap f (Error e) = Error e -- pass through errors fmap f (S x) = S (fx) -- edit successes
So, for example, fmap (+4) (Error "oops") = Error "oops" , while fmap (+4) (S 5) = S 9 .
If this fmap all new for you, why not read the Feature Tutorial ?
Then create an applicative instance. Application allows you to use complex functions, such as simple ones. You need import Control.Applicative at the top of the file for it to work.
instance Applicative Error where pure x = S x -- how to put ordinary data in S f <*> S x = S (fx) Error e <*> _ = Error e _ <*> Error e = Error e
Now, if there were no errors, you must determine
appVal' :: Val -> Val -> Val eval' :: Exp -> Val eval' (App e1 e2) = appVal' (eval' e1) (eval' e2)
Applying, we can use <$> , which is a bit like $ , except that you do everything you specify in fmap . Similarly, <*> works a bit as a function application , with the exception of additional plumbing, so we can determine
eval :: Exp -> Error Val eval (App e1 e2) = appVals <$> eval e1 <*> eval e2
which is a good clean way to handle bugs backstage by focusing on functionality.