Search code examples
haskellderivingvia

How do I make a container for a double a Semigroup?


I am learning Haskell. Imagine I have the following:

data Coordinate = Coordinate double

I wish to implement a semigroup instance for Coordinate.

instance Semigroup (Coordinate a) where
  Coordinate a <> Coordinate b   =  Coordinate (a+b)

The typechecker is displeased at me:

    • Expected kind ‘* -> *’, but ‘Coordinate’ has kind ‘*’
    • In the first argument of ‘Semigroup’, namely ‘(Coordinate a)’
      In the instance declaration for ‘Semigroup (Coordinate a)’
    |
175 | instance (Num a) => Semigroup (Coordinate a) where

(I know that this is just an empty container for a double and I could already be using just the double itself, but I am learning Haskell and I would like to understand how this works.)


Solution

  • The way you specified Coordinate, it doesn't have any type parameters. So the semigroup instance head should be simply

    instance Semigroup Coordinate where
      ...
    

    Alternatively, you can give it a parameter to allow including different number types:

    newtype Coordinate' a = Coordinate' { getCoordinate' :: a }
    

    In this case, the Semigroup instance will need to mention the parameter, however just calling it a won't be enough because you can't perform + on arbitrary types. You need to either restrict it to Double there

    instance Semigroup (Coordinate' Double)
    

    or to an arbitrary numerical type

    instance Num a => Semigroup (Coordinate' a)
    

    Note that in either case, Semigroup may not be the best class for the purpose, consider using AdditiveGroup and then you can also make it a VectorSpace.