A type-safe text formatting library based on combinators.
Call with a formatting function like:
format
- Format to a lazyText
value.sformat
- Format to a strictText
value.bprint
- Format to aBuilder
value.fprint
- Format to stdout.hprint
- Format to aHandle
.
Call syntax:
format <format combinators> <param1> <param2>
Example:
format ("Person's name is " % text % ", age is " % hex) "Dave" 54
or with short-names:
format ("Person's name is " % t % ", age is " % x) "Dave" 54
Similar to C's printf
:
printf("Person's name is %s, age is %x","Dave",54);
and Common Lisp's FORMAT
:
(format nil "Person's name is ~a, age is ~x" "Dave" 54)
> format (text % "!") "Hi!"
"Hi!!"
> format (string % "!") "Hi!"
"Hi!!"
> format int 23
"23"
> format (fixed 0) 23.3
"23"
> format (fixed 2) 23.3333
"23.33"
> format shortest 23.3333
"23.3333"
> format shortest 0.0
"0"
> format sci 2.3
"2.3"
> format (scifmt Fixed (Just 0)) 2.3
"2"
> format commas 123456778
"123,456,778"
> format commas 1234
"1,234"
> format ords 1
"1st"
> format ords 2
"2nd"
> format ords 3
"3rd"
> format ords 4
"4th"
> format hex 15
"f"
> format hex 25
"19"
> 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"
> 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"
> 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"
> format (bytes shortest) 1024
"1KB"
> format (bytes (fixed 2 % " ")) (1024*1024*5)
"5.00 MB"
%.
is like %
but feeds one formatter into another:
λ> format (left 2 '0' %. hex) 10
"0a"
λ> now <- getCurrentTime
λ> format (year % "/" <> month <> "/" % dayOfMonth) now
"2015/01/27"
You can include things verbatim in the formatter:
> format (now "This is printed now.")
"This is printed now."
Although with OverloadedStrings
you can just use string literals:
> format "This is printed now."
"This is printed now."
You can handle things later which makes the formatter accept arguments:
> 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
.
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
:
format :: Format Text a -> a
Because builders are efficient generators.
So in this case we will be expected to produce Builders from arguments:
format . later :: (a -> Builder) -> a -> Text
To do that for common types you can just re-use the formatting library and use bprint:
λ> :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:
> 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
:
> format mint (readMaybe "23")
"23"
> format mint (readMaybe "foo")
""
Although a better, more general combinator might be:
> let mfmt x f = later (maybe x (bprint f))
Now you can use it to maybe format things:
> format (mfmt "Nope!" int) (readMaybe "foo")
"Nope!"
If you're using a type which provides its own builder, like the
Scientific
type:
import Data.Text.Lazy.Builder.Scientific
scientificBuilder :: Scientific -> Builder
formatScientificBuilder :: FPFormat -> Maybe Int -> Scientific -> Builder
Then you can use later
easily:
> 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.