Textual representation of a C++ function I need to create an instance of Show of a somewhat complicated type that represents a C++ function. The "serialization" gives me a Haskell interpretation of the said function.

It works as I wish but it looks utterly horrible, with the string concatenations, point-free conversions and nested let bindings.  How could I improve readability of the code?

instance Show Function where
    show (Function templateArgs returnT fname fArgs) =
        let toLowerIfPolymorphic argName =
               let lcArg = strToLower argName in
                    if lcArg `elem` (map snd templateArgs)
                    then lcArg
                    else id argName in
        concat [
            fname, " :: (",
            intercalate ", " (
                filter (/="") . map (
                \(concept, tIdent) -&gt;
                    if not $ elem concept ["Any", "typename", "class"]
                    then concept ++ " " ++ strToLower tIdent
                    else ""
                ) $ templateArgs    
            ), ") =&gt; ",
            intercalate " -&gt; " (
                map toLowerIfPolymorphic . map (show . fst) $ fArgs
            ),
            (if not $ null fArgs then " -&gt; " else ""),
            toLowerIfPolymorphic . show $ returnT
        ]


The Function is:

data Function = Function {
                    funcTemplateArgs :: [(Concept, Identifier)],
                    funcReturnT      :: CppType,
                    funcName         :: String,
                    funcArgs         :: [(CppType, Identifier)]
                }


CppType is basically a sum-type representing Scalar and Parameterzied types. Just synonyms for tuples. Concept / Identifier is a String synonym. Everything is an instance of Show.

Also I find the if condition then f else id very strange. "Do nothing if false" is somewhat silly.
I would clean this up by bringing what you can to the top level (hiding it using a module export list if need be) as new functions. There are also some sectioning/alias changes that I think would make it more readable.

toLowerIfPolymorphic :: [(Concept, Identifier)] -&gt; Identifier -&gt; Identifier
toLowerIfPolymorphic templateArgs argName
    | lcArg `elem` (snd $ unzip templateArgs) = lcArg
    | otherwise                               = argName
  where
    lcArg = strToLower argName


I often prefer guards to if _ then _ else _, it helps cut down on word soup (and I still don't understand why we don't have boolCase :: Bool -&gt; a -&gt; a -&gt; a in the Prelude). Consider how snd . unzip more specifically captures your intent than using a more powerful but generic higher-order function like map.

The bit with ["Any", "typename", "class"] should work out better with some reordering. Instead of applying a partial transformation with a sentinel value (i.e. filter (not . null) with occasional "" values), drop the junk and then transform the good stuff.

showConstraints :: [(Concept, Identifier)] -&gt; [String]
showConstraints templateArgs = map display . filter valid $ templateArgs
  where
    display (concept, ident) = concept ++ " " ++ strToLower ident
    valid (concept, _) = concept `notElem` ["Any", "typename", "class"]


Then your Show instance ends up looking something like this.

instance Show Function where
  show (Function templateArgs returnT fName fArgs)
     = fName
    ++ " :: ("
    ++ intercalate ", " (showConstraints templateArgs)
    ++ ") =&gt; "
    ++ intercalate " -&gt; " (map (toLowerIfPolymorphic templateArgs . show . fst) $ fArgs)
    ++ (if null fArgs then "" else " -&gt; ")
    ++ toLowerIfPolymorphic templateArgs (show returnT)


I'd probably take this one step further though to handle not having any constraints (in which case you don't want "() =&gt;" to show up, presumably) and write one more helper function.

wrap :: String -&gt; String -&gt; String -&gt; String
wrap _     ""      _   = ""
wrap start content end = start ++ content ++ end


Giving&mdash;

instance Show Function where
  show (Function templateArgs returnT fName fArgs)
      =  fName
      ++ " :: "
      ++ wrap "(" (intercalate ", "   $ showConstraints templateArgs)       ") =&gt; "
      ++ wrap ""  (intercalate " -&gt; " $ map (lowerPoly . show . fst) fArgs) " -&gt; "
      ++ lowerPoly (show returnT)
    where
      lowerPoly = toLowerIfPolymorphic templateArgs