# formatting [](https://travis-ci.org/AJChapman/formatting) [](https://hackage.haskell.org/package/formatting)
Formatting is a type-safe and flexible library for formatting text from built-in or custom data types.
- [Hackage Documentation](https://hackage.haskell.org/package/formatting)
- [The original blog post introducing the library](https://chrisdone.com/posts/formatting/), but note that some of the types have changed: `Holey` is no longer used, and `Format`'s type has changed to `newtype Format r a = Format {runFormat :: (Builder -> r) -> a}`
## Usage
You will probably need the `OverloadedStrings` language extension, and to import `Formatting`:
```haskell
{-# LANGUAGE OverloadedStrings #-}
import Formatting
```
You may also need some or all of these:
```haskell
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TLB
```
Now a simple example:
```haskell
> format ("Person's name is " % text % " and age is " % int) "Dave" 54
"Person's name is Dave and age is 54"
```
In this example, the formatters are two string literals (which take no arguments), and two formatters which take arguments: `text`, which takes a lazy `Text`, and `int` which takes any `Integral`, such as `Int`.
They are all joined together using the `%` operator, producing a formatter which takes two arguments: a lazy `Text` and an `Integral`.
It produces a lazy `Text`, because we used `format`.
To produce other string types, or print the result instead, refer to this table:
| To produce a | use |
| ------------- | ------------------ |
| `TL.Text` | [`format`] |
| `T.Text` | [`sformat`] |
| `Builder` | [`bformat`] |
| `String` | [`formatToString`] |
To print the values instead, refer to this table:
| To print to | use |
| ----------------------------- | ------------- |
| `stdout` | [`fprint`] |
| `stdout`, appending a newline | [`fprintLn`] |
| a handle | [`hprint`] |
| a handle, appending a newline | [`hprintLn`] |
Apart from the `%` operator, formatters can also be joined using the monoid append operator (`<>`) to avoid repeating the same argument, they can be chained using `%.`, and there are also formatter combinators for composing more advanced combinators.
More on this below.
### Formatter Quick Reference
Built-in formatters:
| To format a | e.g. | as | use | short form |
| --------------------------------------:| ------------------------ | ---------------- | ---------------------------------- | ---------- |
| lazy `Text` | `"Hello"` | `"Hello"` | [`text`] | [`t`] |
| strict `Text` | `"World!"` | `"World!"` | [`stext`] | [`st`] |
| `String` | `"Goodbye"` | `"Goodbye"` | [`string`] | [`s`] |
| `Builder` | `"Bathtub"` | `"Bathtub"` | [`builder`] | |
| `Show a => a` | `[1, 2, 3]` | `"[1, 2, 3]"` | [`shown`] | [`sh`] |
| `Char` | `'!'` | `"!"` | [`char`] | [`c`] |
| `Integral a => a` | `23` | `"23"` | [`int`] | [`d`] |
| `Real a => a` | `123.32` | `"123.32"` | [`float`] | [`sf`] |
| `Real a => a` | `123.32` | `"123.320"` | [`fixed`] `3` | [`f`] |
| `Scientific` | `scientific 60221409 16` | `"6.0221409e23"` | [`sci`] | |
| `Scientific` | `scientific 60221409 16` | `"6.022e23"` | [`scifmt`] `Exponent (Just 3)` | |
| `Buildable n, Integral n => n` | `123456` | `"12.34.56"` | [`groupInt`] `2 '.'` | |
| `Buildable n, Integral n => n` | `12000` | `"12,000"` | [`commas`] | |
| `Integral n => n` | `32` | `"32nd"` | [`ords`] | |
| `Num a, Eq a => a` | `1` | `"1 ant"` | `int <>` [`plural`] `"ant" "ants"` | |
| `Num a, Eq a => a` | `2` | `"2 ants"` | `int <>` [`plural`] `"ant" "ants"` | |
| `Enum a => a` | `a` | `"97"` | [`asInt`] | |
| `Integral a => a` | `23` | `"10111"` | [`bin`] | [`b`] |
| `Integral a => a` | `23` | `"0b10111"` | [`prefixBin`] | |
| `Integral a => a` | `23` | `"27"` | [`oct`] | [`o`] |
| `Integral a => a` | `23` | `"0o27"` | [`prefixOct`] | |
| `Integral a => a` | `23` | `"17"` | [`hex`] | [`x`] |
| `Integral a => a` | `23` | `"0x17"` | [`prefixHex`] | |
| `Integral a => a` | `23` | `"13"` | [`base`] `20` | |
| `Buildable a => a` | `10` | `" 10"` | [`left`] `4 ' '` | [`l`] |
| `Buildable a => a` | `10` | `"10 "` | [`right`] `4 ' '` | [`r`] |
| `Buildable a => a` | `10` | `" 10 "` | [`center`] `4 ' '` | |
| `Buildable a => a` | `123456` | `"123"` | [`fitLeft`] `3` | |
| `Buildable a => a` | `123456` | `"456"` | [`fitRight`] `3` | |
| `Buildable a => a` | `True` | `"True"` | [`build`] | |
| `a` | `undefined` | `"gronk!"` | [`fconst`] `"gronk!"` | |
### Formatter Combinator Quick Reference
Formatter combinators take a formatter and modify it somehow, e.g. by using it to format elements of a list, or changing its output.
Built-in formatter combinators:
| To format a | e.g. | as | use |
| ----------------------------------------:| ------------------------ | ----------------------------------- | ---------------------------------------- |
| `Maybe a` | `Nothing` | `"Goodbye"` | [`maybed`] `"Goodbye" text` |
| `Maybe a` | `Just "Hello"` | `"Hello"` | [`maybed`] `"Goodbye" text` |
| `Maybe a` | `Nothing` | `""` | [`optioned`] `text` |
| `Maybe a` | `Just "Hello"` | `"Hello"` | [`optioned`] `text` |
| `Either a b` | `Left "Error!"` | `"Error!"` | [`eithered`] `text int` |
| `Either a b` | `Right 69` | `"69"` | [`eithered`] `text int` |
| `Either a x` | `Left "bingo"` | `"bingo"` | [`lefted`] `text` |
| `Either a x` | `Right 16` | `""` | [`lefted`] `text` |
| `Either x a` | `Right "bingo"` | `"bingo"` | [`righted`] `text` |
| `Either x a` | `Left 16` | `""` | [`righted`] `text` |
| `Foldable t => t a` | `[1, 2, 3]` | `"1st2nd3rd"` | [`concatenated`] `ords` |
| `Foldable t => t a` | `[123, 456, 789]` | `"789456123"` | [`joinedWith`] `(mconcat . reverse) int` |
| `Foldable t => t a` | `[1, 2, 3]` | `"1\|\|2\|\|3"` | [`intercalated`] `"\|\|" int` |
| `Foldable t => t a` | `[1, 2, 3]` | `"1 2 3"` | [`unworded`] `int` |
| `Foldable t => t a` | `[1, 2, 3]` | `"1\n2\n3"` | [`unlined`] `d` |
| `Foldable t => t a` | `[1, 2, 3]` | `"1 2 3"` | [`spaced`] `int` |
| `Foldable t => t a` | `[1, 2, 3]` | `"1,2,3"` | [`commaSep`] `int` |
| `Foldable t => t a` | `[1, 2, 3]` | `"1st, 2nd, 3rd"` | [`commaSpaceSep`] `ords` |
| `Foldable t => t a` | `["one", "two", "three"]` | `"[one, two, three]"` | [`list`] `t` |
| `Foldable t => t a` | `["one", "two", "three"]` | `"[\"one\", \"two\", \"three\"]"` | [`qlist`] `t` |
| `[a]` | `[1..]` | `"[1, 10, 11, 100]"` | [`took`] `4 (list bin)` |
| `[a]` | `[1..6]` | `"[4, 5, 6]"` | [`dropped`] `3 (list int)` |
| `a` | `"one two\tthree\nfour` | `"one, two, three, four"` | [`splat`] `isSpace commaSpaceSep stext` |
| `a` | `1234567890` | `"[123, 456, 789, 0]"` | [`splatWith`] `(chunksOf 3) list int` |
| `a` | `"one,two,three"` | `"one\ntwo\nthree\n"` | [`splatOn`] `"," unlined t` |
| `a` | `"one two three "` | `"[one, two, three]"` | [`worded`] `list text` |
| `a` | `"one\n\ntwo\nthree\n\n` | `"["one", "", "two", "three", ""]"` | [`lined`] `qlist text` |
| `a` | `123456` | `"654321"` | [`alteredWith`] `TL.reverse int` |
| `a` | `"Data.Char.isUpper` | `"DCU"` | [`charsKeptIf`] `isUpper string` |
| `a` | `"Data.Char.isUpper` | `"ata.har.ispper"` | [`charsRemovedIf`] `isUpper string` |
| `a` | `"look and boot"` | `"leek and beet"` | [`replaced`] `"oo" "ee" text` |
| `a` | `"look and boot"` | `"LOOK AND BOOT"` | [`uppercased`] |
| `a` | `"Look and Boot"` | `"look and boot"` | [`lowercased`] |
| `a` | `"look and boot"` | `"Look And Boot"` | [`titlecased`] |
| `a` | `"hellos"` | `"he..."` | [`ltruncated`] `5 text` |
| `a` | `"hellos"` | `"h...s"` | [`ctruncated`] |
| `a` | `"hellos"` | `"...os"` | [`rtruncated`] `5 text` |
| `a` | `1` | `" 1"` | [`lpadded`] `3 int` |
| `a` | `1` | `"1 "` | [`rpadded`] `3 int` |
| `a` | `1` | `" 1 "` | [`cpadded`] `3 int` |
| `a` | `123` | `"123 "` | [`lfixed`] `4 int` |
| `a` | `123456` | `"1..."` | [`lfixed`] `4 int` |
| `a` | `123` | `" 123"` | [`rfixed`] `4 int` |
| `a` | `123456` | `"...6"` | [`rfixed`] `4 int` |
| `a` | `123` | `" 123 "` | [`cfixed`] `2 1 ' ' int` |
| `a` | `1234567` | `"12...7"` | [`cfixed`] `2 1 ' ' int` |
| `a` | `"Goo"` | `"McGoo"` | [`prefixed`] `"Mc" t` |
| `a` | `"Goo"` | `"Goosen"` | [`suffixed`] `"sen" t` |
| `a` | `"Goo"` | `"McGooMc"` | [`surrounded`] `"Mc" t` |
| `a` | `"Goo"` | `"McGoosen"` | [`enclosed`] `"Mc" "sen" t` |
| `a` | `"Goo"` | `"'Goo'"` | [`squoted`] `t` |
| `a` | `"Goo"` | `"\"Goo\""` | [`dquoted`] `t` |
| `a` | `"Goo"` | `"(Goo)"` | [`parenthesised`] `t` |
| `a` | `"Goo"` | `"[Goo]"` | [`squared`] `t` |
| `a` | `"Goo"` | `"{Goo}"` | [`braced`] `t` |
| `a` | `"Goo"` | `"<Goo>"` | [`angled`] `t` |
| `a` | `"Goo"` | ``"`Goo`"`` | [`backticked`] `t` |
| `a` | `"Goo"` | `" Goo"` | [`indented`] `3 t` |
| `Foldable t => t a` | `[1, 2, 3]` | `" 1\n 2\n 3"` | [`indentedLines`] `2 d` |
| `a` | `"1\n2\n3"` | `" 1\n 2\n 3"` | [`reindented`] `2 t` |
| `Integral i, RealFrac d => d` | `6.66` | `"7"` | [`roundedTo`] `int` |
| `Integral i, RealFrac d => d` | `6.66` | `"6"` | [`truncatedTo`] `int` |
| `Integral i, RealFrac d => d` | `6.66` | `"7"` | [`ceilingedTo`] `int` |
| `Integral i, RealFrac d => d` | `6.66` | `"6"` | [`flooredTo`] `int` |
| field through a `Lens' s a` | `(1, "goo")` | `"goo"` | [`viewed`] `_2 t` |
| field through a record accessor `s -> a` | `(1, "goo")` | `"1"` | [`accessed`] `fst d` |
| `Integral a => a` | `4097` | `"0b0001000000000001"` | [`binPrefix`] `16` |
| `Integral a => a` | `4097` | `"0o0000000000010001"` | [`octPrefix`] `16` |
| `Integral a => a` | `4097` | `"0x0000000000001001"` | [`hexPrefix`] `16` |
| `Ord f, Integral a, Fractional f => a` | `1024` | `"1KB"` | [`bytes`] `shortest` |
| `Ord f, Integral a, Fractional f => a` | `1234567890` | `"1.15GB"` | [`bytes`] `(fixed 2)` |
## Composing formatters
`%.` is like `%` but feeds one formatter into another:
``` haskell
λ> format (left 2 '0' %. hex) 10
"0a"
```
## Using more than one formatter on the same argument
``` haskell
λ> now <- getCurrentTime
λ> format (year % "/" <> month <> "/" % dayOfMonth) now
"2015/01/27"
```
## The Buildable Typeclass
One of the great things about `formatting` is that it doesn't rely on typeclasses: you can define one or more formatters for each of your types.
But you also have the option of defining a 'default' formatter for a type, by implementing the `Buildable` typeclass, which has one method: `build :: p -> Builder`.
Once this is defined for a type, you can use the `build` formatter (which is distinct from the `build` method of `Buildable`!):
```haskell
> format ("Int: " % build % ", Text: " % build) 23 "hello"
"Int: 23, Text: hello"
```
Note that while this can be convenient, it also sacrifices some type-safety: there's nothing preventing you from putting the arguments in the wrong order, because both `Int` and `Text` have a `Buildable` instance.
Note also that if a type already has a `Show` instance then you can use this instead, by using the `shown` formatter.
## Understanding the Types
Formatters generally have a type like this:
```haskell
Format r (a -> r)
```
This describes a formatter that will eventually produce some string type `r`, and takes an `a` as an argument.
For example:
```haskell
int :: Integral a => Format r (a -> r)
```
This takes an `Integral a` argument, and eventually produces an `r`.
Let's work through using this with `format`:
```haskell
-- format has this type:
format :: Format TL.Text a -> a
-- so in 'format int', called with an 'Int', 'int's type specialises to:
int :: Format TL.Text (Int -> TL.Text)
-- and 'format's 'a' parameter specialises to 'Int -> TL.Text':
format :: Format TL.Text (Int -> TL.Text) -> Int -> TL.Text
-- so 'format int' takes an Int and produces text:
format int :: Int -> TL.Text
```
What can be confusing in the above is that `int`'s `a` parameter expands to `Int`, but `format`'s `a` parameter expands to `Int -> TL.Text`.
Now let's look at what happens when we use the `%` operator to append formatters:
```haskell
-- Here are the types of the functions we will use:
(%) :: Format r a -> Format r' r -> Format r' a
int :: Format r (Int -> r) -- simplified for this use
stext :: Format r (T.Text -> r)
-- Within the call to '%', in the expression 'int % stext', the type parameters expand like this:
-- r = T.Text -> r'
-- a = Int -> T.Text -> r'
-- and so we have these types:
int :: Format (T.Text -> r') (Int -> T.Text -> r')
stext :: Format r' (T.Text -> r')
int % stext :: Format r' (Int -> T.Text -> r')
-- And so when we use 'format' we get a function that takes two arguments and produces text:
format (int % stext) :: Int -> T.Text -> TL.Text
```
## Comparison with Other Languages
Example:
``` haskell
format ("Person's name is " % text % ", age is " % hex) "Dave" 54
```
or with short-names:
``` haskell
format ("Person's name is " % t % ", age is " % x) "Dave" 54
```
Similar to C's `printf`:
``` c
printf("Person's name is %s, age is %x","Dave",54);
```
and Common Lisp's `FORMAT`:
``` lisp
(format nil "Person's name is ~a, age is ~x" "Dave" 54)
```
## Formatter Examples
### "Hello, World!": Texts
``` haskell
> format (text % "!") "Hi!"
"Hi!!"
> format (string % "!") "Hi!"
"Hi!!"
```
### 123: Integers
``` haskell
> format int 23
"23"
```
### 23.4: Decimals
``` haskell
> format (fixed 0) 23.3
"23"
> format (fixed 2) 23.3333
"23.33"
> format shortest 23.3333
"23.3333"
> format shortest 0.0
"0.0"
> format sci 2.3
"2.3"
> format (scifmt Fixed (Just 0)) 2.3
"2"
```
### 1,242: Commas
``` haskell
> format commas 123456778
"123,456,778"
> format commas 1234
"1,234"
```
### 1st: Ordinals
``` haskell
> format ords 1
"1st"
> format ords 2
"2nd"
> format ords 3
"3rd"
> format ords 4
"4th"
```
### 3F: Hex
``` haskell
> format hex 15
"f"
> format hex 25
"19"
```
### Monday 1st June: Dates & times
``` haskell
> now <- getCurrentTime
> later <- getCurrentTime
> format (dayOfMonth % "/" % month % "/" % year) now now now
"16/06/2014"
> format day now
"167"
> format hms now
"08:24:41"
> format tz now
"+0000"
> format datetime now
"Mon Jun 16 08:24:41 UTC 2014"
> format century now
"20"
> format (dayOfMonthOrd % " of " % monthName) now now
"16th of June"
```
### 3 years ago: Time spans
``` haskell
> format (diff False) (diffUTCTime later now)
"2 seconds"
> format (diff True) (diffUTCTime later now)
"in 2 seconds"
> format (diff True) (diffUTCTime now later)
"2 seconds ago"
> format (seconds 0 % " secs") (diffUTCTime now later)
"2 secs"
```
``` haskell
> let Just old = parseTime defaultTimeLocale "%Y" "1980" :: Maybe UTCTime
> format (years 0) (diffUTCTime now old)
"34"
> format (diff True) (diffUTCTime now old)
"in 35 years"
> format (diff True) (diffUTCTime old now)
"35 years ago"
> format (days 0) (diffUTCTime old now)
"12585"
> format (days 0 % " days") (diffUTCTime old now)
"12585 days"
```
### File sizes
``` haskell
> format (bytes shortest) 1024
"1KB"
> format (bytes (fixed 2 % " ")) (1024*1024*5)
"5.00 MB"
```
### Scientific
If you're using a type which provides its own builder, like the
`Scientific` type:
``` haskell
import Data.Text.Lazy.Builder.Scientific
scientificBuilder :: Scientific -> Builder
formatScientificBuilder :: FPFormat -> Maybe Int -> Scientific -> Builder
```
Then you can use `later` easily:
``` haskell
> format (later scientificBuilder) 23.4
"23.4"
```
Actually, there are now already two handy combinators (`sci` and
`scifmt`) for the `Scientific` type as shown above in the Decimals
section.
## Writing your own Formatters
You can include things verbatim in the formatter:
``` haskell
> format (now "This is printed now.")
"This is printed now."
```
Although with `OverloadedStrings` you can just use string literals:
``` haskell
> format "This is printed now."
"This is printed now."
```
You can handle things later which makes the formatter accept arguments:
``` haskell
> format (later (const "This is printed later.")) ()
"This is printed later."
```
The type of the function passed to `later` should return an instance
of `Monoid`.
``` haskell
later :: (a -> Builder) -> Format r (a -> r)
```
The function you format with (`format`, `bprint`, etc.)
will determine the monoid of choice. In the case of this library, the
top-level formating functions expect you to build a text `Builder`:
``` haskell
format :: Format Text a -> a
```
Because builders are efficient generators.
So in this case we will be expected to produce Builders from arguments:
``` haskell
format . later :: (a -> Builder) -> a -> Text
```
To do that for common types you can just re-use the formatting library
and use bprint:
``` haskell
λ> :t bprint
bprint :: Format Builder a -> a
> :t bprint int 23
bprint int 23 :: Builder
```
Coming back to `later`, we can now use it to build our own printer
combinators:
``` haskell
> let mint = later (maybe "" (bprint int))
> :t mint
mint :: Integral a => Format r (Maybe a -> r)
```
Now `mint` is a formatter to show `Maybe Integer`:
``` haskell
> format mint (readMaybe "23")
"23"
> format mint (readMaybe "foo")
""
```
Although a better, more general combinator might be:
``` haskell
> let mfmt x f = later (maybe x (bprint f))
```
Now you can use it to maybe format things:
``` haskell
> format (mfmt "Nope!" int) (readMaybe "foo")
"Nope!"
```
## Hacking
### Building with Nix
See [README-nix.md](./README-nix.md).
### Running the Tests
From within your `nix-shell`, run `cabal test`.
The tests are in `test/Spec.hs`.
### Running the Benchmarks
Start `nix-shell` like this: `nix-shell --arg doBenchmark true`.
From within your `nix-shell`, run `cabal bench`.
To build the html benchmarking reports, run `cabal bench --benchmark-option=-obench/reports/7.2.0.html > bench/reports/7.2.0.txt`, replacing '7.2.0' with the current version.
This will output the file `bench.html` which you can open in a browser.
The benchmarks are in `bench/bench.hs`.
[`format`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:format
[`sformat`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:sformat
[`bformat`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:bformat
[`formatToString`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:formatToString
[`fprint`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:fprint
[`fprintLn`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:fprintLn
[`hprint`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:hprint
[`hprintLn`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:hprintLn
[`text`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:text
[`stext`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:stext
[`string`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:string
[`builder`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:builder
[`shown`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:shown
[`char`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:char
[`int`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:int
[`float`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:float
[`fixed`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:fixed
[`sci`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:sci
[`scifmt`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:scifmt
[`groupInt`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:groupInt
[`commas`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:commas
[`ords`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:ords
[`plural`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:plural
[`asInt`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:asInt
[`bin`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:bin
[`prefixBin`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:prefixBin
[`oct`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:oct
[`prefixOct`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:prefixOct
[`hex`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:hex
[`prefixHex`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:prefixHex
[`base`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:base
[`left`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:left
[`right`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:right
[`center`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:center
[`fitLeft`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:fitLeft
[`fitRight`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:fitRight
[`build`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:build
[`fconst`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:fconst
[`bytes`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Formatters.html#v:bytes
[`t`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:t
[`st`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:st
[`s`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:s
[`sh`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:sh
[`c`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:c
[`d`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:d
[`sf`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:sf
[`f`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:f
[`b`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:b
[`o`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:o
[`x`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:x
[`l`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:l
[`r`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-ShortFormatters.html#v:r
[`maybed`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:maybed
[`optioned`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:optioned
[`eithered`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:eithered
[`lefted`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:lefted
[`righted`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:righted
[`concatenated`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting.html#v:concatenated
[`joinedWith`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:joinedWith
[`intercalated`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:intercalated
[`unworded`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:unworded
[`unlined`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:unlined
[`spaced`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:spaced
[`commaSep`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:commaSep
[`commaSpaceSep`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:commaSpaceSep
[`list`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:list
[`qlist`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:qlist
[`took`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:took
[`dropped`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:dropped
[`splat`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:splat
[`splatWith`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:splatWith
[`splatOn`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:splatOn
[`worded`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:worded
[`lined`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:lined
[`alteredWith`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:alteredWith
[`charsKeptIf`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:charsKeptIf
[`charsRemovedIf`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#vata.har.isppercharsRemovedIf
[`replaced`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:replaced
[`uppercased`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:uppercased
[`lowercased`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:lowercased
[`titlecased`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:titlecased
[`ltruncated`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:ltruncated
[`ctruncated`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:ctruncated
[`rtruncated`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:rtruncated
[`lpadded`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:lpadded
[`rpadded`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:rpadded
[`cpadded`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:cpadded
[`lfixed`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:lfixed
[`rfixed`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:rfixed
[`cfixed`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:cfixed
[`prefixed`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:prefixed
[`suffixed`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:suffixed
[`surrounded`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:surrounded
[`enclosed`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:enclosed
[`squoted`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:squoted
[`dquoted`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:dquoted
[`parenthesised`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:parenthesised
[`squared`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:squared
[`braced`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:braced
[`angled`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:angled
[`backticked`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:backticked
[`indented`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:indented
[`indentedLines`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:indentedLines
[`reindented`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:reindented
[`roundedTo`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:roundedTo
[`truncatedTo`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:truncatedTo
[`ceilingedTo`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:ceilingedTo
[`flooredTo`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:flooredTo
[`viewed`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:viewed
[`accessed`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:accessed
[`binPrefix`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:binPrefix
[`octPrefix`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:octPrefix
[`hexPrefix`]: https://hackage.haskell.org/package/formatting-7.1.1/docs/Formatting-Combinators.html#v:hexPrefix