vformat-0.12.0.0: A Python str.format() like formatter

Copyright(c) 2019 Version Cloud
LicenseBSD3
MaintainerJorah Gao <jorah@version.cloud>
Stabilityexperimental
Portabilityportable
Safe HaskellSafe
LanguageHaskell2010

Text.Format

Contents

Description

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 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

  1. Can not find argument for the given key.
  2. 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

Note: Since v0.12.0, FormatTime instance has been remove, use vformat-time instead.

Minimal complete definition

Nothing

Methods

formatArg :: a -> Formatter Source #

formatArg :: (Generic a, GFormatArg (Rep a)) => a -> Formatter Source #

Instances
FormatArg Bool Source # 
Instance details

Defined in Text.Format.Class

FormatArg Char Source # 
Instance details

Defined in Text.Format.Class

FormatArg Double Source # 
Instance details

Defined in Text.Format.Class

FormatArg Float Source # 
Instance details

Defined in Text.Format.Class

FormatArg Int Source # 
Instance details

Defined in Text.Format.Class

FormatArg Int8 Source # 
Instance details

Defined in Text.Format.Class

FormatArg Int16 Source # 
Instance details

Defined in Text.Format.Class

FormatArg Int32 Source # 
Instance details

Defined in Text.Format.Class

FormatArg Int64 Source # 
Instance details

Defined in Text.Format.Class

FormatArg Integer Source # 
Instance details

Defined in Text.Format.Class

FormatArg Natural Source # 
Instance details

Defined in Text.Format.Class

FormatArg Word Source # 
Instance details

Defined in Text.Format.Class

FormatArg Word8 Source # 
Instance details

Defined in Text.Format.Class

FormatArg Word16 Source # 
Instance details

Defined in Text.Format.Class

FormatArg Word32 Source # 
Instance details

Defined in Text.Format.Class

FormatArg Word64 Source # 
Instance details

Defined in Text.Format.Class

FormatArg a => FormatArg [a] Source # 
Instance details

Defined in Text.Format.Class

Methods

formatArg :: [a] -> Formatter Source #

formatArgList :: [[a]] -> Formatter

keyOf :: [a] -> ArgKey

FormatArg a => FormatArg ((:=) a) Source # 
Instance details

Defined in Text.Format.Class

FormatArg a => FormatArg (Map Int a) Source # 
Instance details

Defined in Text.Format.Class

FormatArg a => FormatArg (Map String a) Source # 
Instance details

Defined in Text.Format.Class

class FormatType t where Source #

A typeclass provides the variable arguments magic for format

Methods

sfmt :: Format -> Map ArgKey Formatter -> t Source #

Instances
FormatType String Source # 
Instance details

Defined in Text.Format.Class

(FormatArg a, FormatType r) => FormatType (a -> r) Source # 
Instance details

Defined in Text.Format.Class

Methods

sfmt :: Format -> Map ArgKey Formatter -> a -> r Source #

Generic format arg and options

data Options Source #

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

defaultOptions :: Options Source #

Default format options

Options
{ fieldLabelModifier = id
}

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

data Format Source #

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    -> <see ArgKey>
 fmt    -> <see ArgFmt>
 

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>
 
Instances
Eq Format Source # 
Instance details

Defined in Text.Format.Format

Methods

(==) :: Format -> Format -> Bool #

(/=) :: Format -> Format -> Bool #

Show Format Source # 
Instance details

Defined in Text.Format.Format

IsString Format Source # 
Instance details

Defined in Text.Format.Format

Methods

fromString :: String -> Format #

data Format1 Source #

A variant of Format, it transforms all argument's key to Nest (Index 0) key

Instances
Eq Format1 Source # 
Instance details

Defined in Text.Format.Format

Methods

(==) :: Format1 -> Format1 -> Bool #

(/=) :: Format1 -> Format1 -> Bool #

Show Format1 Source # 
Instance details

Defined in Text.Format.Format

IsString Format1 Source # 
Instance details

Defined in Text.Format.Format

Methods

fromString :: String -> Format1 #

data ArgKey Source #

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

Constructors

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 Nest k1 k2, k1 refers to a top-level argument or an attribute (element or field) of a data type, k2 refers an attribute of the data referenced by k1.

Instances
Eq ArgKey Source # 
Instance details

Defined in Text.Format.ArgKey

Methods

(==) :: ArgKey -> ArgKey -> Bool #

(/=) :: ArgKey -> ArgKey -> Bool #

Ord ArgKey Source # 
Instance details

Defined in Text.Format.ArgKey

Read ArgKey Source # 
Instance details

Defined in Text.Format.ArgKey

Show ArgKey Source # 
Instance details

Defined in Text.Format.ArgKey

Semigroup ArgKey Source # 
Instance details

Defined in Text.Format.ArgKey

Monoid ArgKey Source # 
Instance details

Defined in Text.Format.ArgKey

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

data ArgFmt Source #

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 field

    If 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 options

    When 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 by ord 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, see showEFloat
 E               same as "e", but use upper-case E as separator
 f               fixed-point notation see showFFloat
 F               same as "f", but converts nan to NAN and inf to INF
 g               general format, see showGFloat
 G               same as "g", but use upper-case E 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.

Constructors

ArgFmt 

Fields

Instances
Eq ArgFmt Source # 
Instance details

Defined in Text.Format.ArgFmt

Methods

(==) :: ArgFmt -> ArgFmt -> Bool #

(/=) :: ArgFmt -> ArgFmt -> Bool #

Read ArgFmt Source # 
Instance details

Defined in Text.Format.ArgFmt

Show ArgFmt Source # 
Instance details

Defined in Text.Format.ArgFmt

prettyArgFmt :: ArgFmt -> String Source #

Pretty showing ArgFmt

Note: Don't create ArgFmt manually, read from a string, see ArgFmt.

data FmtAlign Source #

A data type indicates how to align arguments

Constructors

AlignNone

Not specified, equivalent to AlignLeft unless number's sign aware enabled.

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.

Instances
Eq FmtAlign Source # 
Instance details

Defined in Text.Format.ArgFmt

Show FmtAlign Source # 
Instance details

Defined in Text.Format.ArgFmt

data FmtSign Source #

A data type indicates how to show number's sign

Constructors

SignNone

Not specified, equivalent to SignMinus for signed numbers.

SignPlus

Sign should be used for both positive as well as negative numbers.

SignMinus

Sign should be used only for negative numbers

SignSpace

A leading space should be used on positive numbers, and a minus sign on negative numbers.

Instances
Eq FmtSign Source # 
Instance details

Defined in Text.Format.ArgFmt

Methods

(==) :: FmtSign -> FmtSign -> Bool #

(/=) :: FmtSign -> FmtSign -> Bool #

Show FmtSign Source # 
Instance details

Defined in Text.Format.ArgFmt

data FmtNumSep Source #

A data type indicates number separator

e.g. 20,200,101 20_200_202

Constructors

NumSepNone

Don't separate number

NumSepDash

Use dash as number separator

NumSepComma

Use comma as number separator

Instances
Eq FmtNumSep Source # 
Instance details

Defined in Text.Format.ArgFmt

Show FmtNumSep Source # 
Instance details

Defined in Text.Format.ArgFmt

data (:=) a infixr 6 Source #

A type represents the top-level named key argument.

Constructors

String := a infixr 6 
Instances
FormatArg a => FormatArg ((:=) a) Source # 
Instance details

Defined in Text.Format.Class

Standard Formatters

formatString :: String -> Formatter Source #

Formatter for string values

Since: 0.11.0

formatChar :: Char -> Formatter Source #

Formatter for Char 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

formatInteger :: Integer -> Formatter Source #

Formatter for Integer values

Since: 0.11.0

formatRealFloat :: RealFloat a => a -> Formatter Source #

Formatter for RealFloat values

Since: 0.11.0

defaultSpecs :: String -> (a -> Formatter) -> a -> Formatter Source #

Use a default specs for the given formatter

Errors

data ArgError Source #

A data type indicates an arg error

Constructors

ArgKeyError

Can not find argument for the given key

ArgFmtError

The field format descriptor does not match the argument

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.

vferror :: String -> a Source #

Raises an error with a vformat-specific prefix on the message string.