Search code examples
haskelltypessemigroup

Make a parametrized data type instance of Semigroup


I want to make the data type Moneda a instance of the Semigroup, and implement the associative operation as +. I'm having difficulties understanding it properly.

The working solution I found, is the following:

data Moneda a = RON Double | EUR Double deriving (Show)

instance Num a => Num (Moneda a) where
    (RON x) + (RON y) = RON (x + y)
    (RON x) + (RON y) = RON (x + y)

instance Semigroup (Moneda a) where
    (<>) = (+)

I couldn't understand why exactly the below aproach is failing and how it's possible to make it work without making Moneda a Num instance.

data Moneda a = RON a | EUR a deriving (Show)

instance Semigroup (Moneda a) where
    (<>) (RON a) (RON b) = (RON a+b) -- fails, please see error below
vs 
    (<>) (RON a) (RON b) = (RON a) -- works but doesn't help me because I need the a+b sum  

-- cannot construct the infinite type: a ~ Moneda a 
-- In the second argument of `(+)', namely `b'

Solution

  • The error you're getting is because you try to add a value of type Moneda a to a value of type a in your implementation of (<>). That is you wrote (RON a+b) which parses as (RON a) + b (because function/constructor application binds stronger than addition). You probably meant to write RON (a + b) as in:

    data Moneda a = RON a | EUR a deriving (Show)
    
    instance Semigroup (Moneda a) where
      (<>) (RON a) (RON b) = RON (a + b)
    

    But that won't work either as it requires addition to be defined on the type to which you apply Moneda. So, for your definition to make sense you will need to constrain the instantiation of the type argument a to types in Num:

    data Moneda a = RON a | EUR a deriving (Show)
    
    instance Num a => Semigroup (Moneda a) where
      RON a <> RON b = RON (a + b)
      EUR a <> EUR b = EUR (a + b)
    

    For example:

    > RON 2 <> RON 3
    RON 5
    

    Note that this definition of (<>) is only partial. It does not account for adding values built with RON to values built with EUR:

    > RON 5 <> EUR 7
    *** Exception: Main.hs:(4,3)-(5,30): Non-exhaustive patterns in function <>
    

    So, you still have to write code that deals with those cases.