How to compare non 'Eq' type in Haskell?

Viewed 121

When loading the following module I get the error:

No instance for (Eq Planet) arising from a use of ‘==’ • In the expression: planet == Mercury In a stmt of a pattern guard for an equation for ‘ageOn’:

How can I check whether planet equals a certain planet?

module SpaceAge (Planet(..), ageOn) where

data Planet = Mercury
        | Venus
        | Earth
        | Mars
        | Jupiter
        | Saturn
        | Uranus
        | Neptune

mercury :: Float
mercury = 0.2408467

venus :: Float
venus = 0.61519726

earth :: Float
earth = 1.0

mars :: Float
mars = 1.8808158

jupiter :: Float
jupiter = 11.862615

saturn :: Float
saturn = 29.447498

uranus :: Float
uranus = 84.016846

neptune :: Float
neptune = 164.79132

ageOn :: Planet -> Float -> Float
ageOn planet seconds
   | planet == Mercury = seconds * mercury
   | planet == Venus = seconds * venus
   | planet == Earth = seconds * earth
   | planet == Mars = seconds * mars
   | planet == Jupiter = seconds * jupiter
   | planet == Saturn = seconds * saturn
   | planet == Uranus = seconds * uranus
   | planet == Neptune = seconds * neptune
2 Answers

You can work with pattern matching:

ageOn :: Planet -> Float -> Float
ageOn Mercury seconds = seconds * mercury
ageOn Venus seconds = seconds * venus
ageOn Earth seconds = seconds * earth
ageOn Mars seconds = seconds * mars
ageOn Jupiter seconds = seconds * jupiter
ageOn Saturn seconds = seconds * saturn
ageOn Uranus seconds = seconds * uranus
ageOn Neptune seconds = seconds * neptune

But you can also make Planet an instance of the Eq typeclass. If you simply want to check if two Planets are the same if you use the same data constructor, you can let Haskell implement this with a deriving clause:

data Planet
  = Mercury
  | Venus
  | Earth
  | Mars
  | Jupiter
  | Saturn
  | Uranus
  | Neptune
  deriving Eq
ageOn planet seconds = seconds * case planet of
  Mercury -> mercury
  Venus -> venus
  Earth -> earth
  Mars -> mars
  ...

Or better yet, factor out the looking up.

yearLength :: Planet -> Float
yearLength = \case
  Mercury -> mercury
  Venus -> venus
  ...

ageOn planet seconds = yearLength planet * seconds

At the same time, I think you might have an error in your logic. If I've lived for 1 Earth year, I've lived for more than 1 Mercury year. So depending on exactly what your function means, you probably want to define

ageOn planet earthYears = earthYears / yearLength planet
Related