7
votes

Suppose I'd want to create a new data type and make the constructors showable, only in lower case instead of their upper case definition. For example:

data Day = Monday | Tuesday | Wednesday| Thursday | Friday | Saturday | Sunday

By adding deriving Show, ghci would print them as "Monday, Tuesday.. etc." To get it to show "monday, tuesday.. etc" I've tried to make a special instance of show:

import Data.Char

strToLower :: [Char] -> [Char]
strToLower (x:xs) = toLower x : strToLower xs
strToLower [] = []

instance Show Day where
   show d = strToLower (show d)

where the first occurrence of show should designate my new amended show function (which will be called everytime I print) whereas for the second I intend the normally derived version of show, to get from the constructor name to a String.

Of course this doesn't work (circular definition) as the ghci has no clue to my separate meanings of the word "show" but I can't figure out how to let him know the distinction, for both versions need to be named show, the first because that's what print calls to and the second because it's a predefined haskell function which can give me a String out of a constructor name. I've tried

show d = strToLower ((showsPrec 0 d) "")

but this comes down to the same circular definition, at least that's what I guess from the ghci getting stuck in a loop.

I understand why constructor names need to begin with an upper case letter, but showing them lower case shouldn't be a problem, should it? I know I could just define my show function for every case separately, e.g. show Monday = "monday" show Tuesday = "tuesday" etc, but I'm only using the days of the week as an example here and my real data type consists of 64 constructors so I think it would be more elegant to solve it differently somehow.

Is it possible to dig into the haskell definition of show and alter a copy of that code? This is the only possible solution I can think of but I don't know how to do it, if it is possible at all. Probably not. So other solutions are very welcome as well!

Thank you for taking your time,

Jelle (Haskell beginner)

3
Why can't you write showDay day = map toLower . show and use showDay instead of show?dave4420
In addition to the other answers, it might be worthwhile to answer your direct question (how can I use the derived show to implement my own show?), for which the answer is "you can't". It is a fundamental rule of Haskell that each class may have at most one instance for a given type.Daniel Wagner

3 Answers

12
votes

You can actually do this using the Typeable and Data classes.

To do this you need the DeriveDataTypeable extension, turn it on with -XDeriveDataTypeable or by putting the following line at the start of the file that defines your type:

{-# LANGUAGE DeriveDataTypeable #-}

You can now import the needed modules:

import Data.Data
import Data.Typeable

And derive Typeable and Data:

data Day = Monday | Tuesday | Wednesday| Thursday | Friday | Saturday | Sunday
     deriving (Typeable, Data)

Now you can use toConstr to get a constructor representation:

instance Show Day where
   show = strToLower . showConstr . toConstr

But see the other answers on whether you'd really want to, instead of simply using a showDay function or your own type class instead.

6
votes

Instances of the Show class are supposed to work in such a way that you can copy-paste its output and use it as Haskell code. If you now have lowercase constructors, these won't work as such. They would work with the newtype approach,

data DayInternal = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday

newtype Day = Day DayInternal
monday = Day Monday
tuesday = Day Tuesday
wednesday = Day Wednesday
thursday = Day Thursday
friday = Day Friday
saturday = Day Saturday
sunday = Day Sunday

instance Show Day where
    show (Day d) = strToLower $ show d

Of course, that leaves you with redundancy you probably don't like. You could build this automatically with Template Haskell, but I doubt that's really worth it.

What I really think is that you want is not a Show instance at all, but rather some kind of pretty-printing. You could roll your own

class MyNiceShow s where
  myNiceShow :: s -> String

data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
      deriving (Show)

instance MyNiceShow Day where
  myNiceShow d = strToLower $ show d
3
votes

Well don't know if you can do that for the same datatype, but a workaround I can see is to wrap it into a newtype as

newtype D = D Day
instance Show D where
    show (D d) = strToLower $ show d

Now you can use type D instead of Day.