Generic programming in Haskell with SYB and ad-hoc polymorphism

Viewed 491

I have a class identical to Show and I would like to make an instance of this class for each tuple type. Usually this is done by writing separately instances for each tuple type

instance  (Show a, Show b) => Show (a,b)  where
  showsPrec _ (a,b) s = show_tuple [shows a, shows b] s

instance (Show a, Show b, Show c) => Show (a, b, c) where
  showsPrec _ (a,b,c) s = show_tuple [shows a, shows b, shows c] s

instance (Show a, Show b, Show c, Show d) => Show (a, b, c, d) where
  showsPrec _ (a,b,c,d) s = show_tuple [shows a, shows b, shows c, shows d] s
...

Writing one instance per tuple type results in a lot of boilerplate and it's easy to see the common pattern shared among all the showPrec implementations. To avoid this kind of boilerplate I thought I could use Data.Generics from Scrap your boilerplate and implement a folding over tuples, like

showTuple = intercalate " " . gmapQ ("" `mkQ` show)

But showTuple doesn't work for some reason

> showTuple (1,2)
" "

I think the problem is the fact that show is polymorphic because if I specialize showTuple then it works

showTupleInt = intercalate " " . gmapQ ("" `mkQ` (show :: Int -> String))
> showTupleInt (1::Int,2::Int)
"1 2"

I have checked the code of gshow that does something similar to what I need, but I can't figure out how it works. If I try to import its code into GHCI I get an error:

> let gshows = (\t -> showChar '('
                      . (showString . showConstr . toConstr $ t)
                      . (foldr (.) id . gmapQ ((showChar ' ' .) . gshows) $ t)
                      . showChar ')'
                      ) `extQ` (shows :: String -> ShowS)
<interactive>:262:59:
Could not deduce (a ~ d)
from the context (Data a)
  bound by the inferred type of
           gshows :: Data a => a -> String -> String
  at <interactive>:(259,5)-(264,44)
or from (Data d)
  bound by a type expected by the context:
             Data d => d -> String -> String
  at <interactive>:262:33-65
  `a' is a rigid type variable bound by
      the inferred type of gshows :: Data a => a -> String -> String
      at <interactive>:259:5
  `d' is a rigid type variable bound by
      a type expected by the context: Data d => d -> String -> String
      at <interactive>:262:33
Expected type: d -> String -> String
  Actual type: a -> String -> String
In the second argument of `(.)', namely `gshows'
In the first argument of `gmapQ', namely
  `((showChar ' ' .) . gshows)'
In the second argument of `(.)', namely
  `gmapQ ((showChar ' ' .) . gshows)'

So I have two questions:

  1. what's wrong with showTuple and how can I fix it such that it will work with tuples of any size
  2. how gshow works and why if I import its code on GHCI I get that error?

EDIT: I'm studying Data.Generics and in general SYM, so I would like to use that module. I will accept an answer only if it uses just that module. Thanks.

3 Answers
Related