Krimson
Krimson

Reputation: 7674

Creating an instance of Num class

I am relatively new to learning haskell.

I have the following abstract data type

data Scalar = 
    Scalar Integer
  deriving (Eq, Show)

I want to be able to do the following operation on the Scaler type:

> (Scalar 10) + 1
> Scalar 11

To do this I tried making the Scalar an instance of the num class like this:

instance Num Scalar where
  (Scalar i1) + i2 = (Scalar (i1+i2))

But this doesn't work. What am I doing wrong? And whats the correct way to do this?

Edit: The error I am getting is:

Couldn't match expected type `Integer' with actual type `Scalar '
In the second argument of `(+)', namely `i2'
In the first argument of `Scalar ', namely `(i1 + i2)'

Upvotes: 1

Views: 9323

Answers (4)

willy-s
willy-s

Reputation: 226

As others have already pointed out the types on both sides of + need to match. What you can do is define a custom operator to add Scalar and Int:

(+.) :: Scalar -> Int -> Scalar
(Scalar i1) +. i2 = Scalar (i1 + i2)

infixl 6 +.

You might also want to use a newtype wrapper instead (all you have to do is replace the data keyword with newtype). This is semantically the same (except in some corner cases with undefined values) but slightly more efficient because newtypes are erased during compilation.

Upvotes: 0

Sibi
Sibi

Reputation: 48766

No, you cannot do that, because the type of + is:

λ> :t (+)
(+) :: Num a => a -> a -> a

So, it operates on the type of same data. In your case you are trying to add a type of Scalar and Integer which isn't valid. You can define an instance like this:

instance Num Scalar where
    (Scalar i1) + (Scalar i2) = Scalar (i1 + i2)

And it will operate on Scalar type:

λ> Scalar 3 + Scalar 4
Scalar 7

But if you really want to do this, you can create your own special function for that:

addNumtoScalar :: Integer -> Scalar -> Scalar
addNumtoScalar x (Scalar y)  = Scalar (x + y)

And then you can add using this function,

λ> addNumtoScalar 3 (Scalar 7)
Scalar 10

Or in an infix fashion:

λ> 3 `addNumtoScalar` (Scalar 7)
Scalar 10

As @user5402 has commented, you can define the fromInteger function of Num typeclass and then use that in your addition. Something like this:

instance Num Scalar where
    (Scalar x) + (Scalar y) = Scalar (x + y)
    fromInteger x = Scalar x

Now, you can use integer literals and they will be automatically converted to Scalar values when necessary, e.g.:

λ> 3 + Scalar 7
Scalar 10

Upvotes: 9

MaxGabriel
MaxGabriel

Reputation: 7705

You can't add a Scalar and an Integer (e.g. Scalar 10 + 1); you should wrap the second value in a Scalar before adding. Thus, your instance should look like this:

instance Num Scalar where
  (Scalar i1) + (Scalar i2) = (Scalar (i1+i2))

However, the Num typeclass provides requires several other methods besides + be implemented. Instead of writing all that manually, you can use the GHC GeneralizedNewtypeDeriving extension to handle all that for you:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype Scalar = Scalar Integer deriving (Eq,Show,Num)

This will automatically derive the Num instance for Scalar based on how Integer implements the Num typeclass. This approach requires you to use a newtype, but it sounds like that's appropriate for your usecase.

Upvotes: 2

Syd Kerckhove
Syd Kerckhove

Reputation: 833

Firstly, there's something wrong with your definition:

(Scalar i1) + i2 = (Scalar (i1+i2))

That should be the following:

(Scalar i1) + (Scalar i2) = Scalar (i1+i2)

Secondly, if you look at the documentation of the Num typeclass

You will see the part about the minimal complete definition.

(+), (*), abs, signum, fromInteger, (negate | (-))

You've implemented (+), but none of the others.

Lastsly, if you post a problem, include the error that you're getting please.

Good luck

Upvotes: 1

Related Questions