Copyright | (c) Justin Le 2019 |
---|---|
License | BSD3 |
Maintainer | justin@jle.im |
Stability | experimental |
Portability | non-portable |
Safe Haskell | None |
Language | Haskell2010 |
Internal workings of the printf mechanisms, exposed for potential debugging purposes.
Please do not use this module for anything besides debugging, as is definitely very unstable and might go away or change dramatically between versions.
Synopsis
- type ParseFmtStr str = EvalParser FmtStrParser str
- type ParseFmtStr_ str = EvalParser_ FmtStrParser str
- type ParseFmt str = EvalParser FFParser str
- type ParseFmt_ str = EvalParser_ FFParser str
- data FormatAdjustment
- type family ShowFormat (x :: k) :: Symbol
- data FormatSign
- data WidthMod
- data Flags = Flags {}
- type EmptyFlags = 'Flags 'Nothing 'Nothing 'False
- data FieldFormat = FF {}
- type SChar = Symbol
- type family Demote k = a | a -> k
- class Reflect (x :: a) where
- class FormatType (t :: SChar) a where
- formatArg :: p t -> a -> FieldFormat -> ShowS
- data PP (c :: SChar) = forall a.FormatType c a => PP a
- class RPrintf (str :: Symbol) ps where
- rprintf_ :: p str -> FormatArgs ps -> String
- type FormatArgs = Rec PP
- class RFormat (ffs :: [Either Symbol FieldFormat]) (ps :: [SChar]) | ffs -> ps where
- rformat :: p ffs -> FormatArgs ps -> ShowS
- class Printf (str :: Symbol) fun where
- printf_ :: p str -> fun
- class FormatFun (ffs :: [Either Symbol FieldFormat]) fun where
- newtype PFmt c = PFmt FieldFormat
- pfmt :: forall c a. FormatType c a => PFmt c -> a -> String
- mkPFmt :: forall str lst ff f w q m c. (Listify str lst, ff ~ ParseFmt_ lst, Reflect ff, ff ~ 'FF f w q m c) => PFmt c
- mkPFmt_ :: forall str lst ff f w q m c p. (Listify str lst, ff ~ ParseFmt_ lst, Reflect ff, ff ~ 'FF f w q m c) => p str -> PFmt c
- newtype PHelp = PHelp {}
Documentation
type ParseFmtStr str = EvalParser FmtStrParser str Source #
type ParseFmtStr_ str = EvalParser_ FmtStrParser str Source #
data FormatAdjustment #
Whether to left-adjust or zero-pad a field. These are
mutually exclusive, with LeftAdjust
taking precedence.
Since: base-4.7.0.0
Instances
Reflect 'ZeroPad Source # | |
Reflect 'LeftAdjust Source # | |
Defined in GHC.TypeLits.Printf.Parse reflect :: p 'LeftAdjust -> Demote a Source # | |
type Demote FormatAdjustment Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type ShowFormat 'ZeroPad Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type ShowFormat 'LeftAdjust Source # | |
Defined in GHC.TypeLits.Printf.Parse |
type family ShowFormat (x :: k) :: Symbol Source #
Instances
data FormatSign #
How to handle the sign of a numeric field. These are
mutually exclusive, with SignPlus
taking precedence.
Since: base-4.7.0.0
Instances
Reflect 'SignSpace Source # | |
Reflect 'SignPlus Source # | |
type Demote FormatSign Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type ShowFormat 'SignSpace Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type ShowFormat 'SignPlus Source # | |
Defined in GHC.TypeLits.Printf.Parse |
Instances
Reflect 'WMhh Source # | |
Reflect 'WMh Source # | |
Reflect 'WMl Source # | |
Reflect 'WMll Source # | |
Reflect 'WML Source # | |
type Demote WidthMod Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type ShowFormat 'WMhh Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type ShowFormat 'WMh Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type ShowFormat 'WMl Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type ShowFormat 'WMll Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type ShowFormat 'WML Source # | |
Defined in GHC.TypeLits.Printf.Parse |
Flags | |
|
Instances
(Reflect d, Reflect i, Reflect l) => Reflect ('Flags d i l :: Flags) Source # | |
type Demote Flags Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type ShowFormat ('Flags a s 'True :: Flags) Source # | |
Defined in GHC.TypeLits.Printf.Parse type ShowFormat ('Flags a s 'True :: Flags) = AppendSymbol (AppendSymbol (ShowFormat a) (ShowFormat s)) "#" | |
type ShowFormat ('Flags a s 'False :: Flags) Source # | |
Defined in GHC.TypeLits.Printf.Parse |
data FieldFormat Source #
Instances
type family Demote k = a | a -> k Source #
Instances
type Demote Bool Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type Demote Nat Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type Demote Symbol Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type Demote FormatAdjustment Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type Demote FormatSign Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type Demote FieldFormat Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type Demote WidthMod Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type Demote Flags Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
type Demote (Maybe a) Source # | |
Defined in GHC.TypeLits.Printf.Parse |
class Reflect (x :: a) where Source #
Instances
Reflect 'False Source # | |
Reflect 'True Source # | |
KnownNat n => Reflect (n :: Nat) Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
KnownSymbol n => Reflect (n :: Symbol) Source # | |
Defined in GHC.TypeLits.Printf.Parse | |
Reflect 'ZeroPad Source # | |
Reflect 'LeftAdjust Source # | |
Defined in GHC.TypeLits.Printf.Parse reflect :: p 'LeftAdjust -> Demote a Source # | |
Reflect 'SignSpace Source # | |
Reflect 'SignPlus Source # | |
Reflect 'WMhh Source # | |
Reflect 'WMh Source # | |
Reflect 'WMl Source # | |
Reflect 'WMll Source # | |
Reflect 'WML Source # | |
(Reflect d, Reflect i, Reflect l) => Reflect ('Flags d i l :: Flags) Source # | |
(Reflect flags, Reflect width, Reflect prec, Reflect mods, Reflect chr) => Reflect ('FF flags width prec mods chr :: FieldFormat) Source # | |
Reflect ('Nothing :: Maybe a) Source # | |
Reflect x => Reflect ('Just x :: Maybe a) Source # | |
class FormatType (t :: SChar) a where Source #
Typeclass associating format types (d
, f
, etc.) with the types
that can be formatted by them.
You can extend the printf methods here for your own types by writing your instances here.
Nothing
formatArg :: p t -> a -> FieldFormat -> ShowS Source #
Instances
Required wrapper around inputs to pprintf
(guarded polyarity). See documentation for
pprintf
for examples of usage.
You can "wrap" any value in PP
as long as it can be formatted as the
format type indicated.
For example, to make a
, you can use PP
"f"
or PP
3.5
, but not PP
94.2
or PP
(3 :: Int)
. To make a value of
type PP
"hello"
, you must wrap a value that can be formatted via PP
cc
.
forall a.FormatType c a => PP a |
class RPrintf (str :: Symbol) ps where Source #
rprintf_ :: p str -> FormatArgs ps -> String Source #
A version of rprintf
taking an explicit
proxy, which allows usage without TypeApplications
>>>
:t rprintf_ (Proxy :: Proxy "You have %.2f dollars, %s")
FormatArgs '["f", "s"] -> String
Instances
(Listify str lst, ffs ~ ParseFmtStr_ lst, RFormat ffs ps) => RPrintf str ps Source # | |
Defined in GHC.TypeLits.Printf.Internal rprintf_ :: p str -> FormatArgs ps -> String Source # |
type FormatArgs = Rec PP Source #
A heterogeneous list (from Data.Vinyl.Core) used for calling with
rprintf
. Instead of supplying the inputs as
different arguments, we can gather all the inputs into a single list to
give to rprintf
.
>>>
:t rprintf @"You have %.2f dollars, %s"
FormatArgs '["f", "s"] -> String
To construct a
, you need to give a value
formattable by FormatArgs
'["f", "s"]f
and a value formattable by s
, given like a linked
list, with :%
for cons and RNil
for nil.
>>>
putStrLn $ rprintf @"You have %.2f dollars, %s" (3.62 :% "Luigi" :% RNil)
You have 3.62 dollars, Luigi
(This should evoke the idea of of 3.62 : Luigi : []
, even though the
latter is not possible in Haskell)
class RFormat (ffs :: [Either Symbol FieldFormat]) (ps :: [SChar]) | ffs -> ps where Source #
rformat :: p ffs -> FormatArgs ps -> ShowS Source #
Instances
RFormat ('[] :: [Either Symbol FieldFormat]) ('[] :: [SChar]) Source # | |
Defined in GHC.TypeLits.Printf.Internal rformat :: p '[] -> FormatArgs '[] -> ShowS Source # | |
(KnownSymbol str, RFormat ffs ps) => RFormat (('Left str :: Either Symbol FieldFormat) ': ffs) ps Source # | |
Defined in GHC.TypeLits.Printf.Internal | |
(Reflect ff, ff ~ 'FF f w p m c, RFormat ffs ps) => RFormat (('Right ff :: Either Symbol FieldFormat) ': ffs) (c ': ps) Source # | |
Defined in GHC.TypeLits.Printf.Internal |
class Printf (str :: Symbol) fun where Source #
printf_ :: p str -> fun Source #
A version of printf
taking an explicit
proxy, which allows usage without TypeApplications
>>>
putStrLn $ printf_ (Proxy :: Proxy "You have %.2f dollars, %s") 3.62 "Luigi"
You have 3.62 dollars, Luigi
Instances
(Listify str lst, ffs ~ ParseFmtStr_ lst, FormatFun ffs fun) => Printf str fun Source # | |
Defined in GHC.TypeLits.Printf.Internal |
class FormatFun (ffs :: [Either Symbol FieldFormat]) fun where Source #
The typeclass supporting polyarity used by
printf
. It works in mostly the same way as
PrintfType
from Text.Printf, and similar the same as
FormatF
.
Ideally, you will never have to run into this typeclass or have to deal
with it. It will come up if you ask for the type of
printf
, or sometimes if you give the wrong number
or type of arguments to it.
>>>
:t printf @"You have %.2f dollars, %s"
FormatFun '[ Right ..., 'Left " dollars ", 'Right ...] fun => fun
Every item in the first argument of FormatFun
is a chunk of the
formatting string, split between format holes (Right
) and string
chunks (Left
). You can successively "eliminate" them by providing
more arguments that implement each hole:
>>>
:t printf @"You have %.2f dollars, %s" 3.62
FormatFun '[ Right ...] fun => fun
Until you you finally fill all the holes:
>>>
:t printf @"You have %.2f dollars, %s" 3.62 "Luigi"
FormatFun '[] t => t
at which point you may use it as a String
or
, in the same
way that Text.Printf works. We also support using strict IO
()Text
lazy Text
as well.
So, while it's possible to reason with this using the types, it's
usually more difficult than with pprintf
and rprintf
.
This is why, instead of reasoning with this using its types, it's easier to reason with it using the errors instead:
>>>
printf @"You have %.2f dollars, %s"
-- ERROR: Call to printf missing argument fulfilling "%.2f" -- Either provide an argument or rewrite the format string to not expect -- one.
>>>
printf @"You have %.2f dollars, %s" 3.62
-- ERROR: Call to printf missing argument fulfilling "%s" -- Either provide an argument or rewrite the format string to not expect -- one.
>>>
printf @"You have %.2f dollars, %s" 3.62 "Luigi"
You have 3.62 dollars, Luigi
>>>
printf @"You have %.2f dollars, %s" 3.62 "Luigi" 72
-- ERROR: An extra argument of type Integer was given to a call to printf -- Either remove the argument, or rewrite the format string to include the -- appropriate hole.
If you're having problems getting the error messages to give helpful
feedback, try using pHelp
:
>>>
pHelp $ printf @"You have %.2f dollars, %s" 3.62
-- ERROR: Call to printf missing argument fulfilling "%s" -- Either provide an argument or rewrite the format string to not expect -- one.
pHelp
can give the type system the nudge it needs to provide good
errors.
Instances
(TypeError ('Text "Result type of a call to printf not sufficiently inferred." :$$: 'Text "Please provide an explicit type annotation or other way to help inference.") :: Constraint) => FormatFun ('[] :: [Either Symbol FieldFormat]) () Source # | |
Defined in GHC.TypeLits.Printf.Internal | |
a ~ Char => FormatFun ('[] :: [Either Symbol FieldFormat]) Text Source # | |
a ~ Char => FormatFun ('[] :: [Either Symbol FieldFormat]) Text Source # | |
a ~ Char => FormatFun ('[] :: [Either Symbol FieldFormat]) PHelp Source # | |
a ~ Char => FormatFun ('[] :: [Either Symbol FieldFormat]) [a] Source # | |
Defined in GHC.TypeLits.Printf.Internal | |
a ~ () => FormatFun ('[] :: [Either Symbol FieldFormat]) (IO a) Source # | |
(TypeError ((('Text "An extra argument of type " :<>: 'ShowType a) :<>: 'Text " was given to a call to printf.") :$$: 'Text "Either remove the argument, or rewrite the format string to include the appropriate hole") :: Constraint) => FormatFun ('[] :: [Either Symbol FieldFormat]) (a -> b) Source # | |
Defined in GHC.TypeLits.Printf.Internal | |
(KnownSymbol str, FormatFun ffs fun) => FormatFun (('Left str :: Either Symbol FieldFormat) ': ffs) fun Source # | |
(TypeError (MissingError ff) :: Constraint) => FormatFun (('Right ff :: Either Symbol FieldFormat) ': ffs) PHelp Source # | |
(TypeError (MissingError ff) :: Constraint) => FormatFun (('Right ff :: Either Symbol FieldFormat) ': ffs) Text Source # | |
(TypeError (MissingError ff) :: Constraint) => FormatFun (('Right ff :: Either Symbol FieldFormat) ': ffs) Text Source # | |
(TypeError (MissingError ff) :: Constraint) => FormatFun (('Right ff :: Either Symbol FieldFormat) ': ffs) () Source # | |
(TypeError (MissingError ff) :: Constraint) => FormatFun (('Right ff :: Either Symbol FieldFormat) ': ffs) String Source # | |
(TypeError (MissingError ff) :: Constraint) => FormatFun (('Right ff :: Either Symbol FieldFormat) ': ffs) (IO a) Source # | |
(Reflect ff, ff ~ 'FF f w p m c, FormatType c a, FormatFun ffs fun) => FormatFun (('Right ff :: Either Symbol FieldFormat) ': ffs) (a -> fun) Source # | |
Utility type powering pfmt
. See dcumentation for pfmt
for more
information on usage.
Using OverloadedLabels, you never need to construct this directly
can just write #f
and a
will be generated. You can also
create this using PFmt
"f"mkPFmt
or mkPFmt_
, in the situations where
OverloadedLabels doesn't work or is not wanted.
pfmt :: forall c a. FormatType c a => PFmt c -> a -> String Source #
Parse and run a single format hole on a single vale. Can be useful
for formatting individual items or for testing your own custom instances of
FormatType
.
Usually meant to be used with OverloadedLabels:
>>>
pfmt #f 3.62
"3.62"
However, current versions of GHC disallow labels that aren't valid
identifier names, disallowing things like
. While
there is an
<https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0170-unrestricted-overloadedlabels.rst
approved proposal> that allows this, if you are using an earlier GHC
version, you can get around this using pfmt
#.2f 3.62mkPFmt
:
>>>
pfmt (mkPFmt @".2f") 3.6234124
"3.62"
Ideally we'd want to be able to write
>>>
pfmt #.2f 3.6234124
"3.62"
(which should be possible in GHC 8.10+)
Note that the format string does not include the leading %
.
mkPFmt :: forall str lst ff f w q m c. (Listify str lst, ff ~ ParseFmt_ lst, Reflect ff, ff ~ 'FF f w q m c) => PFmt c Source #
Useful for using pfmt
without OverloadedLabels, or also when
passing format specifiers that aren't currently allowed with
OverloadedLabels until GHC 8.10+ (like #.2f
).
>>>
pfmt (mkPFmt @".2f") 3.6234124
"3.62"
mkPFmt_ :: forall str lst ff f w q m c p. (Listify str lst, ff ~ ParseFmt_ lst, Reflect ff, ff ~ 'FF f w q m c) => p str -> PFmt c Source #
A version of mkPFmt
that takes an explicit proxy input.
>>>
pfmt (mkPFmt_ (Proxy :: Proxy ".2f") 3.6234124
"3.62"
A useful token for helping the type system give useful errors for
printf
:
>>>
printf @"You have ".2f" dollars, %s" 3.26 :: PHelp
-- ERROR: Call to printf missing argument fulfilling "%s" -- Either provide an argument or rewrite the format string to not expect -- one.
Usually things should work out on their own without needing this ... but sometimes the type system could need a nudge.
See also pHelp
PHelp | |
|