Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Indent all lines in a string

Tags:

string

haskell

I have some types with custom Show instances defined. They are structured like this:

data TopLevel = TopLevel SndLevel
data SndLevel = SndLevel Int

instance Show SndLevel where
  show (SndLevel i) = "SndLevel: \n\t" ++ (show i)

My Show instance for SndLevel produces nice looking strings that look like the following when they appear in my output:

SndLevel: 
  5

I would like to create a Show instance for topLevel that causes TopLevel (SndLevel 5) to look like this when printed to the terminal:

TopLevel
  SndLevel
    5

I was hoping to find a function built into Haskell that would add "\t" at the front of a string and before each location where "\n" appears in that string.

The best solution I found would go along the lines of the answer in this post. In this case, I would replace "\n" with "\t\n".

I assume I'm not the first person to need Show instances for hierarchically organized data in Haskell, so I would like to know if there is a more idiomatic way to get this done. Is there some better solution to my problem?

p.s: I realize this kind of printing is not the best for the example datatypes I use above. The real datatypes I want to write instances for are product types, and so they don't read well when stretched out on one line. With that in mind, if there is a popular way to deal with this kind of problem without newlines and tabs, that could also solve my problem.

like image 213
Kevin Bradner Avatar asked May 20 '19 20:05

Kevin Bradner


Video Answer


1 Answers

We can solve this by using lines :: String -> [String] and unlines :: [String] -> String to move from a String to a list of Strings and back.

In between, we can make use of map :: (a -> b) -> [a] -> [b] to prepend all lines (a String is a list of Chars) with a tab, like:

indent :: String -> String
indent = unlines . map ('\t' :) . lines

For example:

Prelude> indent (show (SndLevel 5))
"\tSndLevel: \n\t\t5\n"

We can use this in our defintion of Show for both SndLevel and TopLevel like:

instance Show SndLevel where
    show (SndLevel n) = "SndLevel:" ++ '\n' : indent (show n)

instance Show TopLevel where
    show (TopLevel n) = "TopLevel:" ++ '\n' : indent (show n)

This thus gives us:

Prelude> print (TopLevel (SndLevel 5))
TopLevel:
    SndLevel:
        5

That being said, a Show is usually used to show a representation of the object that can usually be "injected" back inh the compiler/interpreter. The idea of using indentation is not bad at all, but perhaps it makes sence to define your own typeclass for that. You could make that typeclass more efficient by using a parameter that is passed and updated, that keeps track of the indentation level.

There are furthermore several "pretty printing" libraries [Reddit] that can print the structure of an object nicely. So instead of "reinventing the wheel", it might be worth using one of the packages listed on the Reddit page.

like image 151
Willem Van Onsem Avatar answered Oct 12 '22 19:10

Willem Van Onsem