SlowerPhoton
SlowerPhoton

Reputation: 998

Data type doesn't work inside function argument

Suppose I have this data type:

data Zinf = NegInf | Z Integer | PosInf deriving (Eq, Ord, Show)

It is also Num, since all necessary operations are defined. When I type for example PosInf + 1, the interpret returns PosInf.

Now I derive yet another data type:

data Interval = Simple Integer | Complicated Zinf Zinf deriving (Eq, Show)

Now, since Zinf is also Num, I can write down the following:

(Z 3) + 4

and I get

(Z 7)

I can also test

(Z 3) + 6 == 9

and the interpret returns True.

I can also write Complicated 3 (6*7) to get Complicated (Z 3) (Z 42).

Finally, I create a tree with these "Interval" data types:

data BinTree a = Empty | Node a (BinTree a) (BinTree a) deriving (Eq, Show)
type ITree = BinTree Interval

Now, when I try to write this function:

moveBy :: ITree -> Integer -> ITree
moveBy Empty _ = Empty
moveBy (Node (Simple a) l r) i = (Node (Simple (a+i)) (moveBy l i) (moveBy r i))
moveBy (Node (Complicated a b) l r) i = (Node ( Complicated (a + i) (b + i) ) (moveBy l i) (moveBy r i)) 

it produces the following error:

* Couldn't match expected type `Zinf' with actual type `Integer'                                                         
* In the second argument of `(+)', namely `i'                                                                             
In the second argument of `Complicated', namely `(b + i)'                                                                     
In the first argument of `Node', namely `(Complicated (a + i) (b + i))'

How come? b should be of type Zinf, b+i again of type Zinf, since i is Integer. And Complicated expects exactly this kind of argument.

Upvotes: 1

Views: 73

Answers (2)

Daniel Wagner
Daniel Wagner

Reputation: 153102

When you write

Z 3 + 4

it does not mean

Z 3 + (4 :: Integer)

but

Z 3 + (4 :: Zinf)

and uses fromInteger :: Integer -> Zinf (implemented by you in your Num instance) to interpret the 4 as a Zinf. Indeed, addition must always be between two values of the same type -- say, two Zinfs, or two Integers -- never between a Zinf and an Integer.

You may modify your moveBy to take a Zinf argument, so that addition is fine:

moveBy :: ITree -> Zinf -> ITree
-- implementation as in the question

Or you may convert the Integer given to moveBy to a Zinf in the same way as is being implicitly done when you write Z 3 + 4:

moveBy :: -- as in the question
moveBy (Node (Simple a) l r) i = Node (Simple (a+fromInteger i)) (moveBy l i) (moveBy r i)
-- and similarly for the Complicated case

Upvotes: 4

leftaroundabout
leftaroundabout

Reputation: 120731

Now, since Zinf is also Num, I can write down the following:

(Z 3) + 4

Yes, but it's important that (+) has in this case not the type Zinf -> Integer -> Zinf, as it may seem. You may be thinking 4 has type integer there, but it doesn't: it actually has type Zinf. This is surprising since 3 does have type integer!

What's going on: Haskell number literals are polymorphic.

Prelude> :t 1
1 :: Num p => p

So, a number literal can have type Integer or Float or indeed Zinf, depending on what the context demands. In (Z 3) + 4 this is Zinf for 4, so 4 has type Zinf there. Great!

However, this does not work in Simple (a+i), because there the type of i is fixed by the signature / context to be Integer. Therefore you can't add it to a Zinf.

What you can do however: any Integer value can be “upgraded” into a polymorphic number (like number literals) by wrapping it in fromInteger. So, what you want there is

moveBy (...) i = (Node (Simple (a + fromInteger i)) (...) (...)

or equivalently Simple (a + Z i).


Actually what's going on with number literals is that they also just wrap the “plain” integer literal in fromInteger, to make it polymorphic.

Upvotes: 8

Related Questions