Reputation: 5410
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
.
Upvotes: 1
Views: 589
Reputation: 49218
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 Expr
essions 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.
Upvotes: 3