XellaM
XellaM

Reputation: 68

How to write the instance for Show of a given datatype shorter?

I´m quite new to Haskell but I wonder how I can write following Code shorter:

data Suite = Club | Heart | Spade | Diamond 

data Value = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | 
King | Ace

data Card = Card Suite Value

instance Show Suite where
    show Club = "Club"
    show Heart = "Heart"
    show Spade = "Spade"
    show Diamond = "Diamond"

instance Enum Suite where
    enumFromTo Club Diamond = [Club, Heart, Spade, Diamond]
    enumFromTo Heart Diamond = [Heart, Spade, Diamond]
    enumFromTo Club Spade = [Club, Heart, Spade]

instance Show Value where
    show Two = "Two"
    show Three = "Three"
    show Four = "Four"
    show Five = "Five"
    show Six = "Six"
    show Seven = "Seven"
    show Eight = "Eight"
    show Nine = "Nine"
    show Ten = "Ten"
    show Jack = "Jack"
    show Queen = "Queen"
    show King = "King"
    show Ace = "Ace"

I want to write the instance for Show Value way shorter. Is there a good way to do this or do I need to write all of it? I also wonder how i could go from here if I want to define the same instances for Eq Card, Ord Card? So far

instance Eq Card where
    Card _ _ == _ = False

instance Ord Card where
    Card Heart Three > Card Heart Two = True

worked, but to write every single possibility would be quite a lot of work. Thanks for any answers!

Edit: I´m aware of the possiblity to append deriving (Show, etc..) but I don´t want to use it

Upvotes: 3

Views: 118

Answers (3)

Alexey Romanov
Alexey Romanov

Reputation: 170899

In some cases, such as Instance Show Value, there is no good way to shorten it without deriving (not counting the ones in dfeuer's answer).

But in others there is! E.g.

  1. for the Enum instances it's enough to define fromEnum and toEnum, all the rest have default definitions. You certainly don't need to list all possibilities in enumFromTo as your example code does.

  2. After you define instance Enum Value, you can write comparison functions by converting to Int and comparing the results:

    instance Eq Value where
      x == y = fromEnum x == fromEnum y
    
    instance Ord Value where
      compare x y = compare (fromEnum x) (fromEnum y)
    
  3. You can use instances for Value and Suit when writing definitions for Card, e.g.

    instance Eq Card where
      Card s1 v1 == Card s2 v2 = s1 == s2 && v1 == v2
    

Upvotes: 2

dfeuer
dfeuer

Reputation: 48631

You've rejected deriving these instances, which is the main way we avoid that much boilerplate. The most obvious remaining elementary way to shorten the Show Value is to use a case expression. This uses an extra line but shortens each case slightly:

instance Show Value where
    show x = case x of
      Two -> "Two"
      Three -> "Three"
      -- etc.

Expanding to non-elementary ways, you could

  1. Use generics, either the somewhat more modern version in GHC.Generics or (probably easier in this case) the one in Data.Data. For these, you'll need deriving Generic or deriving Data, respectively, and then you can write (or dig up on Hackage) generic versions of the class methods you need. Neither of these approaches seems very appropriate for a Haskell beginner, but you can work up to them over a number of months.

  2. Use Template Haskell. This is a very advanced language feature, and despite working with Haskell for many years, I have not really begun to grasp how to program with it. Good luck!

Upvotes: 6

If you just want your show method call to print the name of the constructor (as it appears here), there's no need to manually instance them at all. You can automatically derive the Show instance thusly:

data Suit = Club | Heart | Spade | Diamond
    deriving Show

data Value = Two | Three | Four | Five | Six | Seven 
    | Eight | Nine | Ten | Jack | Queen | King | Ace
    deriving Show

Upvotes: 4

Related Questions