Copyright | (c) 2019 Version Cloud |
---|---|
License | BSD3 |
Maintainer | Jorah Gao <jorah@version.cloud> |
Stability | experimental |
Portability | portable |
Safe Haskell | Safe |
Language | Haskell2010 |
This library is inspired by Python's str.format and Haskell's Text.Printf, and most of the features are copied from these two libraries.
Synopsis
- format :: FormatType r => Format -> r
- format1 :: FormatArg a => Format1 -> a -> String
- class FormatArg a where
- class FormatType t where
- data Options
- defaultOptions :: Options
- fieldLabelModifier :: Options -> String -> String
- genericFormatArg :: (Generic a, GFormatArg (Rep a)) => Options -> a -> Formatter
- type Formatter = ArgKey -> ArgFmt -> Either SomeException String
- data Format
- data Format1
- data ArgKey
- topKey :: ArgKey -> ArgKey
- popKey :: ArgKey -> ArgKey
- data ArgFmt = ArgFmt {}
- prettyArgFmt :: ArgFmt -> String
- data FmtAlign
- data FmtSign
- data FmtNumSep
- data (:=) a = String := a
- formatString :: String -> Formatter
- formatChar :: Char -> Formatter
- formatInt :: (Integral a, Bounded a) => a -> Formatter
- formatWord :: (Integral a, Bounded a) => a -> Formatter
- formatInteger :: Integer -> Formatter
- formatRealFloat :: RealFloat a => a -> Formatter
- data ArgError
- errorArgKey :: String -> a
- errorArgFmt :: String -> a
- vferror :: String -> a
Format functions
format :: FormatType r => Format -> r Source #
Format a variable number of arguments with Python-style format string
>>>
format "{:s}, {:d}, {:.4f}" "hello" 123 pi
"hello, 123, 3.1416">>>
format "{1:s}, {0:d}, {2:.4f}" 123 "hello" pi
"hello, 123, 3.1416">>>
format "{:s} {:d} {pi:.4f}" "hello" 123 ("pi" := pi)
"hello, 123, 3.1416"
See Format
to learn more about format string syntax.
See FormatArg
to learn how to derive FormatArg for your own data types.
format1 :: FormatArg a => Format1 -> a -> String Source #
A variant of format
, it takes only one positional argument
>>>
:set -XDeriveGeneric
>>>
import GHC.Generics
>>>
data Triple = Triple String Int Double deriving Generic
>>>
instance FormatArg Triple
>>>
format "{0!0:s} {0!1:d} {0!2:.4f}" $ Triple "hello" 123 pi
"hello, 123, 3.1416">>>
format1 "{0:s} {1:d} {2:.4f}" $ Triple "hello" 123 pi
"hello, 123, 3.1416"
Classes
class FormatArg a where Source #
Typeclass of formatable values.
The formatArg
method takes a value, a key and a field format descriptor and
either fails due to a ArgError
or produce a string as the result.
There is a default formatArg
for Generic
instances, which applies
defaultOptions
to genericFormatArg
.
There are two reasons may cause formatting fail
- Can not find argument for the given key.
- The field format descriptor does not match the argument.
Extending to new types
Those format functions can be extended to format types other than those
provided by default. This is done by instantiating FormatArg
.
Examples
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} import Control.Exception import GHC.Generics import Text.Format -- Manually extend to () instance FormatArg () where formatArg x k fmt@(ArgFmt{fmtSpecs="U"}) = let fmt' = fmt{fmtSpecs = ""} in formatArg (show x) k fmt' formatArg _ _ _ = Left $ toException ArgFmtError -- Use default generic implementation for type with nullary data constructors. data Color = Red | Yellow | Blue deriving Generic instance FormatArg Color -- Use default generic implementation for type with non-nullary data constructor. data Triple = Triple String Int Double deriving Generic instance FormatArg Triple -- Use default generic implementation for type using record syntax. data Student = Student { no :: Int , name :: String , age :: Int } deriving Generic instance FormatArg Student -- Customize field names data Book = Book { bookName :: String , bookAuthor :: String , bookPrice :: Double } instance FormatArg Book where formatArg x k fmt | k == mempty = return $ format1 "{name} {author} {price:.2f}" x | k == Name "name" = formatArg (bookName x) mempty fmt | k == Name "author" = formatArg (bookAuthor x) mempty fmt | k == Name "price" = formatArg (bookPrice x) mempty fmt | otherwise = Left $ toException $ ArgKeyError -- A better way to customize field names -- instance FormatArg Book where -- formatArg = genericFormatArg $ -- defaultOptions { fieldLabelModifier = drop 4 } main :: IO () main = do putStrLn $ format "A unit {:U}" () putStrLn $ format "I like {}." Blue putStrLn $ format "Triple {0!0} {0!1} {0!2}" $ Triple "Hello" 123 pi putStrLn $ format1 "Student: {no} {name} {age}" $ Student 1 "neo" 30 putStrLn $ format "A book: {}" $ Book "Math" "nobody" 99.99 putStrLn $ format1 "Book: {name}, Author: {author}, Price: {price:.2f}" $ Book "Math" "nobody" 99.99
Nothing
formatArg :: a -> Formatter Source #
formatArg :: (Generic a, GFormatArg (Rep a)) => a -> Formatter Source #
Instances
FormatArg Bool Source # | |
FormatArg Char Source # | |
FormatArg Double Source # | |
FormatArg Float Source # | |
FormatArg Int Source # | |
FormatArg Int8 Source # | |
FormatArg Int16 Source # | |
FormatArg Int32 Source # | |
FormatArg Int64 Source # | |
FormatArg Integer Source # | |
FormatArg Natural Source # | |
FormatArg Word Source # | |
FormatArg Word8 Source # | |
FormatArg Word16 Source # | |
FormatArg Word32 Source # | |
FormatArg Word64 Source # | |
FormatTime t => FormatArg t Source # | Default specs is "%Y-%m-%dT%H:%M:%S", see |
Defined in Text.Format.Class | |
FormatArg a => FormatArg [a] Source # | |
Defined in Text.Format.Class | |
FormatArg a => FormatArg ((:=) a) Source # | |
FormatArg a => FormatArg (Map Int a) Source # | |
FormatArg a => FormatArg (Map String a) Source # | |
class FormatType t where Source #
A typeclass provides the variable arguments magic for format
Generic format arg and options
Options that specify how to format your datatype
Options can be set using record syntax on defaultOptions with the fields below.
Since: 0.11.0
genericFormatArg :: (Generic a, GFormatArg (Rep a)) => Options -> a -> Formatter Source #
A configurable generic Formatter
creator.
Since: 0.11.0
Data types
A data type indicates a format string
Format string contains "replacement fields" surrounded by curly braces {}. Anything that is not contained in braces is considered literal text, which is copied unchanged to the output. If you need to include a brace character in the literal text, it can be escaped by doubling {{ and }}.
Format string syntax
format -> {chars | ("{" [key][":"fmt] "}")} key -> <seeArgKey
> fmt -> <seeArgFmt
>
Note: This library use a description language to describe syntax, see next section.
Note: A key can be omitted only if there is no explict index key before it, it will be automatically caculated and inserted to the format string according to its position in the omitted key sequence.
Examples
>>>
"I like {coffee}, I drink it everyday." :: Format
>>>
"{no:<20} {name:<20} {age}" :: Format
>>>
"{{\"no\": {no}, \"name\": \"{name}\"}}" :: Format
Syntax description language
A syntax expr may contain a list of fields as followings
identifier identifier of an expr <description> use natural language as an expr -> use right hand expr to describe identifier () a required field, may be omitted [] an optional field {} repeat any times of the field | logical or, choice between left and right "" literal text
Built-in exprs
char -> <any character> chars -> {char} int -> <integer without sign>
A variant of Format
,
it transforms all argument's key to Nest (Index 0) key
A data type indicates key of format argument
The key syntax
key -> [(int | chars) {"!" (int | chars)}]
Since the "!" is used to seprate keys, if you need to include a "!" in a named key, it can be escaped by doubling "!!".
Note: See Format
to learn more about syntax description language
Examples
>>>
read "0" :: ArgKey
>>>
read "country" :: ArgKey
>>>
read "coun!!try" :: ArgKey
>>>
read "country!name" :: ArgKey
>>>
read "country!cities!10!name" :: ArgKey
Index Int | Refers to a top-level positional argument or an element in an list-like data type. |
Name String | Refers to a top-level named argument or a field of a record data type. |
Nest ArgKey ArgKey | For |
topKey :: ArgKey -> ArgKey Source #
Extract the topmost indexed or named key from a key
>>>
topKey (read "k1!k2!k3") == Name "k1"
True>>>
topKey (read "name") == Name "name"
True>>>
topKey (read "123") == Index 123
True>>>
topKey mempty
*** Exception: vformat: empty arg key
popKey :: ArgKey -> ArgKey Source #
Remove the topmost indexed or named key from a key
>>>
popKey (read "k1!k2!k3") == read "k2!k3"
True>>>
popKey (read "name") == mempty
True>>>
popKey (read "123") == mempty
True>>>
popKey mempty
*** Exception: vformat: empty arg key
A data type indicates how to format an argument.
The format syntax
fmt -> [[pad] align][sign]["#"]["0"][width][sep]["." precision][specs]
pad -> char
align -> "<" | ">" | "^" | "="
sign -> "+" | "-" | " "
width -> int | ("{" key "}")
sep -> "_" | ","
precision -> int | ("{" key "}")
specs -> chars
key -> <see ArgKey
>
#
will cause the "alternate form" to be used for integers.The alternate format is defined differently for different types.
- add 0b prefix for binary
- add 0o prefix for octal
- add 0x prefix for hexadecimal
with
indicates minimum with of the fieldIf omitted, the field width will be determined by the content.
When align is omitted, preceding width by a zero character enables sign-aware zero-padding for numbers. This is equivalent to a pad of 0 with an align of =.
precision
indicates how many digits should be displayed after the decimal point for a floating point number, or maximum width for other types.When precision is omitted
- preceding dot is present, indicates precision is 0
- preceding dot is omitted too, indicates precision not set, default value (i.e. 6 for floating point numbers, 0 for others) will be used.
specs
indicates type specified optionsWhen specs is omitted, the default specs will be used. The default specs is defined differently from different types.
Examples
>>>
read "*<30s" :: ArgFmt
>>>
read "<10.20s" :: ArgFmt
>>>
read "0=10_.20d" :: ArgFmt
>>>
read "#010_.20b" :: ArgFmt
String specs
s default s
Integer specs
b binary format integer c char point (Char
will be trasformed byord
first) d decimal format integer o octal format integer x hex format integer (use lower-case letters) X hex format integer (use upper-case letters) default d
Floating point number specs
e exponent notation, seeshowEFloat
E same as "e", but use upper-caseE
as separator f fixed-point notation seeshowFFloat
F same as "f", but converts nan to NAN and inf to INF g general format, seeshowGFloat
G same as "g", but use upper-caseE
as separator and converts nan to NAN and inf to INF % percentage, same as "f" except multiplies 100 first and followed by a percent sign default g
See FormatArg
to learn how to define specs for your own types.
ArgFmt | |
|
prettyArgFmt :: ArgFmt -> String Source #
A data type indicates how to align arguments
AlignNone | Not specified, equivalent to |
AlignLeft | Forces the argument to be left-aligned within the available space. |
AlignRight | Forces the field to be right-aligned within the available space. |
AlignCenter | Forces the field to be centered within the available space. |
AlignSign | Number specified, forces the padding to be placed after the sign (if any) but before the digits. |
A data type indicates how to show number's sign
A data type indicates number separator
e.g. 20,200,101 20_200_202
NumSepNone | Don't separate number |
NumSepDash | Use dash as number separator |
NumSepComma | Use comma as number separator |
A type represents the top-level named key argument.
Standard Formatters
formatString :: String -> Formatter Source #
Formatter for string values
Since: 0.11.0
formatInt :: (Integral a, Bounded a) => a -> Formatter Source #
Formatter for Int
values
Since: 0.11.0
formatWord :: (Integral a, Bounded a) => a -> Formatter Source #
Formatter for Word
values
Since: 0.11.0
formatRealFloat :: RealFloat a => a -> Formatter Source #
Formatter for RealFloat
values
Since: 0.11.0
Errors
A data type indicates an arg error
ArgKeyError | Can not find argument for the given key |
ArgFmtError | The field format descriptor does not match the argument |
Instances
Eq ArgError Source # | |
Show ArgError Source # | |
Exception ArgError Source # | |
Defined in Text.Format.Error toException :: ArgError -> SomeException # fromException :: SomeException -> Maybe ArgError # displayException :: ArgError -> String # |
errorArgKey :: String -> a Source #
Calls vferror
to indicate an arg key error for a given type.
errorArgFmt :: String -> a Source #
Calls vferror
to indicate an arg format error for a given type.