{- Acknowledgements ~~~~~~~~~~~~~~~~~~~ * 'prefixF', 'suffixF', 'padBothF', 'groupInt' are taken from <https://hackage.haskell.org/package/formatting> Written by Github user @mwm <https://github.com/mwm> * 'ordinalF' is taken from <https://hackage.haskell.org/package/formatting> Written by Chris Done <https://github.com/chrisdone> * 'atBase' is taken from <https://hackage.haskell.org/package/formatting>, originally from <https://hackage.haskell.org/package/lens> Seems to be written by Johan Kiviniemi <https://github.com/ion1> -} module Fmt ( -- * Overloaded strings -- $overloadedstrings -- * Examples -- $examples -- * Migration guide from @formatting@ -- $migration -- * Basic formatting -- $brackets -- ** Ordinary brackets -- $god1 (+|), (|+), -- ** 'Show' brackets -- $god2 (+||), (||+), -- ** Combinations -- $god3 (|++|), (||++||), (|++||), (||++|), -- * Old-style formatting format, formatLn, Format, -- * Helper functions fmt, fmtLn, pretty, prettyLn, Builder, Buildable(..), -- * Formatters -- ** Time module Fmt.Time, -- ** Text indentF, indentF', nameF, unwordsF, unlinesF, -- ** Lists listF, listF', blockListF, blockListF', jsonListF, jsonListF', -- ** Maps mapF, mapF', blockMapF, blockMapF', jsonMapF, jsonMapF', -- ** Tuples tupleF, -- ** ADTs maybeF, eitherF, -- ** Padding/trimming prefixF, suffixF, padLeftF, padRightF, padBothF, -- ** Hex hexF, -- ** Bytestrings base64F, base64UrlF, -- ** Integers ordinalF, commaizeF, -- *** Base conversion octF, binF, baseF, -- ** Floating-point floatF, exptF, fixedF, -- ** Conditional formatting whenF, unlessF, -- ** Generic formatting genericF, ) where import Formatting.Buildable (Buildable(..)) import Data.Text.Lazy.Builder import Fmt.Internal import Fmt.Time -- $setup -- >>> :set -XOverloadedStrings -- >>> :set -XDeriveGeneric -- >>> import Data.Text (Text) {- $overloadedstrings You need @OverloadedStrings@ enabled to use this library. There are three ways to do it: * __In GHCi:__ do @:set -XOverloadedStrings@. * __In a module:__ add @\{\-\# LANGUAGE OverloadedStrings \#\-\}@ to the beginning of your module. * __In a project:__ add @OverloadedStrings@ to the @default-extensions@ section of your @.cabal@ file. -} {- $examples Here's a bunch of examples because some people learn better by looking at examples. Insert some variables into a string: >>> let (a, b, n) = ("foo", "bar", 25) >>> ("Here are some words: "+|a|+", "+|b|+"\nAlso a number: "+|n|+"") :: String "Here are some words: foo, bar\nAlso a number: 25" Print it: >>> fmtLn ("Here are some words: "+|a|+", "+|b|+"\nAlso a number: "+|n|+"") Here are some words: foo, bar Also a number: 25 Format a list in various ways: >>> let xs = ["John", "Bob"] >>> fmtLn ("Using show: "+||xs||+"\nUsing listF: "+|listF xs|+"") Using show: ["John","Bob"] Using listF: [John, Bob] >>> fmt ("YAML-like:\n"+|blockListF xs|+"") YAML-like: - John - Bob >>> fmt ("JSON-like: "+|jsonListF xs|+"") JSON-like: [ John , Bob ] -} {- $migration Instead of using @%@, surround variables with '+|' and '|+'. You don't have to use @sformat@ or anything else, and also where you were using @build@, @int@, @text@, etc in @formatting@, you don't have to use anything in @fmt@: @ formatting __sformat ("Foo: "%build%", bar: "%int) foo bar__ fmt __"Foo: "+|foo|+", bar: "+|bar|+""__ @ The resulting formatted string is polymorphic and can be used as 'String', 'Text', 'Builder' or even 'IO' (i.e. the string will be printed to the screen). However, when printing it is recommended to use 'fmt' or 'fmtLn' for clarity. @fmt@ provides lots of formatters (which are simply functions that produce 'Builder'): @ formatting __sformat ("Got another byte ("%hex%")") x__ fmt __"Got another byte ("+|hexF x|+")"__ @ Instead of the @shown@ formatter, either just use 'show' or double brackets: @ formatting __sformat ("This uses Show: "%shown%") foo__ fmt #1 __"This uses Show: "+|show foo|+""__ fmt #2 __"This uses Show: "+||foo||+""__ @ Many formatters from @formatting@ have the same names in @fmt@, but with added “F”: 'hexF', 'exptF', etc. Some have been renamed, though: @ __Cutting:__ fitLeft -\> 'prefixF' fitRight -\> 'suffixF' __Padding:__ left -\> 'padLeftF' right -\> 'padRightF' center -\> 'padBothF' __Stuff with numbers:__ ords -\> 'ordinalF' commas -\> 'commaizeF' @ Also, some formatters from @formatting@ haven't been added to @fmt@ yet. Specifically: * @plural@ and @asInt@ (but instead of @asInt@ you can use 'fromEnum') * @prefixBin@, @prefixOrd@, @prefixHex@, and @bytes@ * formatters that use @Scientific@ (@sci@ and @scifmt@) They will be added later. (On the other hand, @fmt@ provides some useful formatters not available in @formatting@, such as 'listF', 'mapF', 'tupleF' and so on.) -} ---------------------------------------------------------------------------- -- Documentation for operators ---------------------------------------------------------------------------- {- $brackets To format strings, put variables between ('+|') and ('|+'): >>> let name = "Alice" :: String >>> "Meet "+|name|+"!" :: String "Meet Alice!" Of course, 'Text' is supported as well: >>> "Meet "+|name|+"!" :: Text "Meet Alice!" You don't actually need any type signatures; however, if you're toying with this library in GHCi, it's recommended to either add a type signature or use 'fmtLn': >>> fmtLn ("Meet "+|name|+"!") Meet Alice! Otherwise the type of the formatted string would be resolved to @IO ()@ and printed without a newline, which is not very convenient when you're in GHCi. On the other hand, it's useful for quick-and-dirty scripts: @ main = do [fin, fout] \<- words \<$\> getArgs __"Reading data from "+|fin|+"\\n"__ xs \<- readFile fin __"Writing processed data to "+|fout|+"\\n"__ writeFile fout (show (process xs)) @ Anyway, let's proceed. Anything 'Buildable', including numbers, booleans, characters and dates, can be put between ('+|') and ('|+'): >>> let starCount = "173" >>> fmtLn ("Meet "+|name|+"! She's got "+|starCount|+" stars on Github.") Meet Alice! She's got 173 stars on Github. Since the only thing ('+|') and ('|+') do is concatenate strings and do conversion, you can use any functions you want inside them. In this case, 'length': >>> fmtLn (""+|name|+"'s name has "+|length name|+" letters") Alice's name has 5 letters If something isn't 'Buildable', just use 'show' on it: >>> let pos = (3, 5) >>> fmtLn ("Character's position: "+|show pos|+"") Character's position: (3,5) Or one of many formatters provided by this library – for instance, for tuples of various sizes there's 'tupleF': >>> fmtLn ("Character's position: "+|tupleF pos|+"") Character's position: (3, 5) Finally, for convenience there's the ('|++|') operator, which can be used if you've got one variable following the other: >>> let (a, op, b, res) = (2, "*", 2, 4) >>> fmtLn (""+|a|++|op|++|b|+" = "+|res|+"") 2*2 = 4 Also, since in some codebases there are /lots/ of types which aren't 'Buildable', there are operators ('+||') and ('||+'), which use 'show' instead of 'build': prop> (""+|show foo|++|show bar|+"") == (""+||foo||++||bar||+"") -} -- $god1 -- Operators for the operators god! -- $god2 -- More operators for the operators god! {- $god3 Z̸͠A̵̕͟͠L̡̀́͠G̶̛O͝ ̴͏̀ I͞S̸̸̢͠ ̢̛͘͢C̷͟͡Ó̧̨̧͞M̡͘͟͞I̷͜N̷̕G̷̀̕ (Though you can just use @""@ between @+| |+@ instead of using these operators, and 'Show'-brackets don't have to be used at all because there's 'show' available.) -} ---------------------------------------------------------------------------- -- TODOs ---------------------------------------------------------------------------- {- docs ~~~~~~~~~~~~~~~~~~~~ * mention that fmt doesn't do the neat thing that formatting does with (<>) (or maybe it does? there's a monoid instance for functions after all, though I might also have to write a IsString instance for (a -> Builder)) * write that if +| |+ are hated or if it's inconvenient in some cases, you can just use provided formatters and <> (add Fmt.DIY for that?) (e.g. "pub:" <> base16F foo) * write that it can be used in parallel with formatting? can it, actually? * clarify what exactly is hard about writing `formatting` formatters -} {- others ~~~~~~~~~~~~~~~~~~~~ * what effect does it have on compilation time? what effect do other formatting libraries have on compilation time? -}