3

I'm attempting to define an evaluator for the language E and to be quite frank I'm completely at a loss for how to fix all the errors I keep getting with how the eval type is defined. I've spent several hours now reading up on interpreters, monads and trying to find something similar to give me a basis but I've turned up nothing. This is homework so naturally no straight out answers please. My big problems right now are the fact that there are no instance declarations for Num E or Integral E and when I've tried using fromInt and fromNum to fix this I'm met with additional errors. I've also tried changing the definitions to all sorts of different ways, the main problem with those being that Int doesn't match the type of E. I feel like I'm missing something pretty basic but I haven't been able to narrow it down at all. I'll be happy to answer any other questions if I wasn't clear on any particular points. If there are any sources that would be good additional information I'd really appreciate links.

data E = IntLit Int
   | BoolLit Bool
   | Plus E E
   | Minus E E
   | Multiplies E E
   | Divides E E
   | Equals E E
     deriving (Eq, Show)

eval :: E -> E
--eval = undefined
eval (IntLit a) = IntLit a
eval (BoolLit a) = BoolLit a
eval (Plus a b) = eval a + eval b
eval (Minus a b) = eval a - eval b
eval (Multiplies a b) = eval a * eval b
eval (Divides a b) = eval a `div` eval b
eval (Equals a b) = BoolLit(a == b)
  • 1
    what should your `eval` really do? Maybe a quick look at this question/answer (spoiler: mine - sorry) can help you as it seems to be in the same direction: https://stackoverflow.com/questions/25968409/operations-with-user-defined-datatype/25969082#25969082 – Random Dev Oct 05 '14 at 19:39
  • BTW: I think that most likely you should eval to either a `Bool` or an `Int` and you can do this without using `+` on `E` itself if you just recurse on the subexpressions and patternmatch it's evaluation (is it a `BoolLit` or a `IntLit` or something else? I demonstrated this is the answer linked above. – Random Dev Oct 05 '14 at 19:42
  • Well done for the recursive call to `eval` - you're thinking along the right lines, so I'm upvoting. I'll also close this as a duplicate of that question, since @CarstenKönig has a good, detailed answer there which is what you need to do. – AndrewC Oct 05 '14 at 19:44
  • Thank you all for the quick input! Carsten's thread is exactly what I was looking for. – JustKeepSwimming Oct 05 '14 at 19:53
  • are you ok with us putting this on hold as a duplicate then? - nervermind Andrew already did ;) – Random Dev Oct 05 '14 at 19:56
  • Duplicate is more than fine. Thank you again for putting together such a comprehensive explanation in the other thread. It was exactly what I'd been missing. – JustKeepSwimming Oct 05 '14 at 20:00
  • @CarstenKönig I disagree about evaluating to Int or Bool, unless you mean BoolLit or BoolLit. You could down the `E content` route, but I'm not sure it's best under the circumstances. – AndrewC Oct 05 '14 at 20:04
  • @user4111252 You'll find that generally you get a fairly timely response on Stack Overflow, so it's worth hanging about like you did to respond to comments etc. Also, well done for the great attitude you've shown. – AndrewC Oct 05 '14 at 20:06
  • 1
    @AndrewC - yes I was talking about that (I think the answer shows this a bit better) - sadly I cannot edit my comment any more – Random Dev Oct 05 '14 at 20:25

1 Answers1

3

Monads have really nothing to do with this. Because you are mixing two types together: ints and bools, you either need to use some type hackery (GADTs) and define eval with type:

eval :: E a -> a

or define a new type called Value like this:

data Value = IntValue Int | BoolValue Bool | TypeError

and then have:

eval :: E -> Value

Inside eval, you need to match results of your expressions like this:

eval (Plus e1 e2) = case eval e1 of
    (IntValue v1) -> case eval e2 of
        (IntValue v2) -> IntValue (v1+v2)
        _ -> TypeError
    _ -> TypeError

This is tedious, but simple. :) Of course you don't want to repeat yourself many times, so save yourself a lot of work by defining a helper function:

evalMathBinOp :: (Int -> Int -> Int) -> E -> E -> Value
evalMathBinOp f e1 e2 = case eval e1 of
    (IntValue v1) -> case eval e2 of
        (IntValue v2) -> IntValue (f v1 v2)
        _ -> TypeError
    _ -> TypeError

and now just:

eval (Plus e1 e2) = evalMathBinOp (+) e1 e2
eval (Minus e1 e2) = evalMathBinOp (-) e1 e2
-- and so on...
Piotr Miś
  • 981
  • 5
  • 6
  • ^^ I think GADTs a probably a bit hefty for this kind of homework ;) (most likely the data-type `E` and the the signature of `eval` will be fixed by the excercise - it surely is by the question) – Random Dev Oct 05 '14 at 19:47
  • Yeah, that's why I only mentioned how it's done in the real world. :) – Piotr Miś Oct 05 '14 at 19:47
  • Thanks for the quick input! I'll be sure to keep this in mind in case we are ever given a bit more leeway to fiddle with the problems in the future. – JustKeepSwimming Oct 05 '14 at 19:51