Reputation: 9380
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)
Upvotes: 2
Views: 170
Reputation: 11923
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
-- (...)
Upvotes: 5
Reputation: 17806
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
.
Upvotes: 3