views:

102

answers:

1

Exercise 14.16-17 in Thompson asks me to add the operations of multiplication and (integer) division to the type Expr, which represents a simple language for arithmetic, then define the functions show and eval (evaluates an expression of type Expr) for Expr.

My solution works for each arithmetic operation except division:

data Expr = L Int
          | Expr :+ Expr
          | Expr :- Expr
          | Expr :* Expr
          | Expr :/ Expr

instance Num Expr where
 (L x) + (L y) = L (x + y)
 (L x) - (L y) = L (x - y)
 (L x) * (L y) = L (x * y)

instance Eq Expr where
 (L x) == (L y) = x == y

instance Show Expr where
 show (L n) = show n
 show (e1 :+ e2) = "(" ++ show e1 ++ " + " ++ show e2 ++ ")"
 show (e1 :- e2) = "(" ++ show e1 ++ " - " ++ show e2 ++ ")"
 show (e1 :* e2) = "(" ++ show e1 ++ " * " ++ show e2 ++ ")"
 show (e1 :/ e2) = "(" ++ show e1 ++ " / " ++ show e2 ++ ")"

eval :: Expr -> Expr
eval (L n) = L n
eval (e1 :+ e2) = eval e1 + eval e2
eval (e1 :- e2) = eval e1 - eval e2
eval (e1 :* e2) = eval e1 * eval e2

E.g.,

*Main> (L 6 :+ L 7) :- L 4
  ((6 + 7) - 4)
*Main> it :* L 9
  (((6 + 7) - 4) * 9)
*Main> eval it
  81
  it :: Expr

However, I am running into problems when I try to implement division. I don't understand the error message I receive when I try to compile the following:

instance Integral Expr where
 (L x) `div` (L y) = L (x `div` y)

eval (e1 :/ e2) = eval e1 `div` eval e2

This is the error:

Chapter 14.15-27.hs:19:9:

No instances for (Enum Expr, Real Expr)
  arising from the superclasses of an instance declaration
               at Chapter 14.15-27.hs:19:9-21
Possible fix:
  add an instance declaration for (Enum Expr, Real Expr)
In the instance declaration for `Integral Expr'

In the first place, I have no idea why defining div for the data type Expr requires me to define an instance of Enum Expr or Real Expr.

+3  A: 

Well, that's the way the Integral typeclass is defined. For information, you can e.g. just type :i Integral into GHCi.

You'll get

class (Real a, Enum a) => Integral a where ...

which means any type a that should be Integral has to be Real and Enum first. C'est la vie.


Note that maybe you've got your types messed up quite a bit. Take a look at

instance Num Expr where
 (L x) + (L y) = L (x + y)
 (L x) - (L y) = L (x - y)
 (L x) * (L y) = L (x * y)

This just allows you to add Expressions if they wrap plain numbers. I'm pretty sure you don't want that. You want to add arbitrary expressions and you already have a syntax for this. It's just

instance Num Expr where
  (+) = (:+)
  (-) = (:-)
  -- ...

This allows you to write (L 1) + (L 2) with perfectly normal syntax. Likewise, eval should not just reduce expressions but yield a number, and therefore have the type eval :: Expr -> Integer. Division is simple for that matter

eval (a :/ b) = (eval a) `div` (eval b)

which is defined since you just divide numbers.

Dario
So the function 'div' doesn't require overloading certain functions for Real or Enum, but being an instance of Integral requires being an instance of Real and Enum? Okay, thanks.
danportin
No, `div` is just available as the instance method of class `Integral` and therefore requires your type to be `Real` and `Enum` too. But read my edited answer ... you probably don't want that.
Dario
Well, I know that I could have defined an operator c as (c) = (:c) in an instance declaration for Num Expr, and that 'eval' is supposed to return an Int; but that exercise was more boring than having eval return a value of type Expr. If that isn't what you mean, though, then perhaps I 'really do' have my types messed up.
danportin
I don't quite get that you mean with your operator c, but just define `eval :: Expr -> Int` and I'm pretty sure it'll work ;)
Dario
I guess I'm dense, I just realized what you meant in your edit, although I don't understand why I have my types 'messed up.' Thanks for your help!
danportin