Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing the "default show" in Haskell?

Say you have a data-structure (borrowed from this question):

data Greek = Alpha | Beta | Gamma | Delta | Eta | Number Int

Now one can make it an instance of Show by appending deriving Show on that instruction.

Say however we wish to show Number Int as:

instance Show Greek where
    show (Number x) = show x
    -- ...

The problem is that one must specify all other parts of the Greek data as well like:

    show Alpha = "Alpha"
    show Beta = "Beta"

For this small example that's of course doable. But if the number of options is long, it requires a large amount of work.

I'm wondering whether it is possible to access the "default show" implementation and call it with a wildcard. For instance:

instance Show Greek where
    show (Number x) = show x
    show x = defaultShow x

You thus "implement" the specific patterns that differ from the default approach and the remaining patterns are resolved by the "fallback mechanism".

Something a bit similar to method overriding with a reference to super.method in object oriented programming.

like image 426
Willem Van Onsem Avatar asked Feb 23 '15 02:02

Willem Van Onsem


2 Answers

As @phg pointed above in the comment this can be also done with the help of generic-deriving:

{-# LANGUAGE DeriveGeneric #-}
module Main where

import           Generics.Deriving.Base (Generic)
import           Generics.Deriving.Show (GShow, gshow)

data Greek = Alpha | Beta | Gamma | Delta | Eta | Number Int
  deriving (Generic)

instance GShow Greek
instance Show Greek where
  show (Number n) = "n:" ++ show n
  show l = gshow l

main :: IO ()
main = do
  print (Number 8)
  print Alpha
like image 138
paluh Avatar answered Sep 22 '22 17:09

paluh


You can sorta accomplish this using Data and Typeable. It is a hack of course, and this example only works for "enumerated" types as in your example.

I'm sure we could get more elaborate with how we do this, but to cover your given example:

{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data
import Data.Typeable

data Greek = Alpha | Beta | Gamma | Delta | Eta | Number Int 
             deriving (Data,Typeable)

instance Show Greek where
    show Number n = show n
    show x = show $ toConstr x

This approach as I've implemented it cannot handle nested data structures or anything else remotely fancy, but again, this is an ugly hack. If you really must use this approach you can dig around in the Data.Data package I'm sure you could piece something together...

Here is a blog post giving a quick introduction to the packages: http://chrisdone.com/posts/data-typeable

The proper way to go about this would be to use a newtype wrapper. I realize that this isn't the most convenient solution though, especially when using GHCi, but it incurs no additional overhead, and is less likely to break in unexpected ways as your program grows.

data Greek = Alpha | Beta | Gamma | Delta | Eta | Number Int 
         deriving (Show)

newtype SpecialPrint = SpecialPrint Greek

instance Show SpecialPrint where
    show (SpecialPrint (Number x)) = "Number: " ++ show x
    show (SpecialPrint x) = show x

main = do
    print (SpecialPrint Alpha)
    print (SpecialPrint $ Number 1)
like image 32
danem Avatar answered Sep 24 '22 17:09

danem