2
votes

Hello while doing examples from Real World Haskell book i have encountered this example and i can not understand what it does mean and how it does work:
instance Num a=>Num (SymbolicManip a)
In this case i should translate to somehting like : "For Num instance of type SymbolicManip there is a constraint regarding its field of type a,which is : a being an instance of Num itself"? Can someone please tell me if i interpreted it right or explain?
Why wouldn't instance Num (SymbolicManip a) be enough?

-- The "operators" that we're going to support
data Op = Plus | Minus | Mul | Div | Pow
        deriving (Eq, Show)

{- The core symbolic manipulation type -}
data SymbolicManip a = 
          Number a           -- Simple number, such as 5
        | Arith Op (SymbolicManip a) (SymbolicManip a)
          deriving (Eq, Show)

{- SymbolicManip will be an instance of Num.  Define how the Num
operations are handled over a SymbolicManip.  This will implement things
like (+) for SymbolicManip. -}
instance Num a => Num (SymbolicManip a) where
    a + b = Arith Plus a b
    a - b = Arith Minus a b
    a * b = Arith Mul a b
    negate a = Arith Mul (Number (-1)) a
    abs a = error "abs is unimplemented"
    signum _ = error "signum is unimplemented"
    fromInteger i = Number (fromInteger i)

P.S All code is from the book (chapter 13 - subchapter-Extended Example-Numeric Types)

2
Try compiling the code without the Num constraint and see what happens.Paul Johnson

2 Answers

5
votes

It's important to see that a SymbolicManip a cannot be an instance of Num without a also being an instance of Num So, just like when we add constraints to functions, we can add a constraint to a typeclass:

 instance Num a => Num (SymbolicManip a) where
 --       ^^^^^^^^ "As long as `a` is an instance of `Num`..."
 --                ^^^^^^^^^^^^^^^^^^^^^ "...so is `SymbolicManip a`"

We must include the Num a => constraint, because in the implementation, we use fromInteger to produce members of the type a. This is unavoidable, just like adding a Num constraint to the function example a b = 2*a + b, i.e. example :: Num a => a -> a -> a.

Here's a simpler example. Consider this type:

newtype Identity a = Identity a

Note that an Identity a could be an instance of Num, so long as a is a Num too, so, we add a constraint:

instance Num a => Num (Identity a) where
-- (...)
3
votes

It means that if a is an instance of Num then SybolicManip a is also an instance of Num.

So if you have:

x :: SymbolicManip Integer
y :: SymbolicManip Integer

Then you can write x+y without needing to define what that means. But if instead you tried to add two values of SymbolicManip String you would get a type error, because String is not an instance of Num.

If you look at the instance you will see that negate and fromInteger both use features of Num. Hence if you took the constraint out then the compiler would give errors about not being able to deduce that a is an instance of Num.