Felix Hohnstein
Felix Hohnstein

Reputation: 539

How to add a new function to an exsiting Type class in Haskell?

I am currently trying to understand Haskell Type-Classes. After creating the simple example below and seeing that it works ....

data MyMaybe a = MyNothing | MyJust a
    deriving(Show)

class IncreaseByOne a where
    addOne :: a -> a

instance (Num a) => IncreaseByOne (MyMaybe a) where
    addOne  MyNothing = (MyNothing)
    addOne (MyJust x) = (MyJust (x+1))

asked my self: Is it possible to add the function addOne to the existing Type-Class Num. So all Numbers can make use of the function addOne. Something like:

instance IncreaseByOne Num where
    addOne x = x+1 
    

Upvotes: 0

Views: 717

Answers (2)

Silvio Mayolo
Silvio Mayolo

Reputation: 70287

It's also worth noting that you can just define a free-standing function. Unlike in Java or C#, where free functions (erm, static functions) are different and are called differently than instance functions, in Haskell there's really no difference. So, if all you want to do is make a function that works for all Num, here it is.

addOne :: Num a => a -> a
addOne = (+1)

No typeclasses necessary. That type signature literally reads as "for all numbers a, takes an a and returns an a".

Upvotes: 3

leftaroundabout
leftaroundabout

Reputation: 120711

Well, you can in principle write instances like

{-# LANGUAGE UndecidableInstances #-}
instance (Num a) => IncreaseByOne a where
  addOne x = x+1     -- or `addOne = (+1)`

The -XUndecidableInstances is not really a problem, but what's a problem is that this instance overlaps with any other instances you might want to declare, specifically with your MyMaybe one. The reason there's a problem there is that nothing in principle stops you from also having, maybe only later on in a module further downstream,

instance Num a => Num (MyMaybe a) where ...

and then both the Num a => IncreaseByOne a and Num a => IncreaseByOne (MyMaybe a) would apply.

GHC can do overlapping instances, but it's usually a bad idea. I would suggest you instead just write out short instances for all the concrete types that are relevant. To make this extra concise, you can have a default implementation based on Num:

{-# LANGUAGE DefaultSignatures #-}

class IncreaseByOne a where
  addOne :: a -> a
  default addOne :: Num a => a -> a
  addOne = (+1)

instance (Num a) => IncreaseByOne (MyMaybe a) where
  addOne = fmap (+1)   -- `MyMaybe` _should_ have a `Functor` instance!

instance IncreaseByOne Int       -- note no instance body is needed,
instance IncreaseByOne Integer   -- because the default implementation
instance IncreaseByOne Double    -- applies

Upvotes: 6

Related Questions