typelits-printf 0.1.1.0 → 0.2.0.0
raw patch · 5 files changed
+106/−367 lines, 5 filesdep −vinylPVP ok
version bump matches the API change (PVP)
Dependencies removed: vinyl
API changes (from Hackage documentation)
- GHC.TypeLits.Printf: PP :: a -> PP (c :: SChar)
- GHC.TypeLits.Printf: [RNil] :: forall u (a :: u -> Type). Rec a ('[] :: [u])
- GHC.TypeLits.Printf: data PP (c :: SChar)
- GHC.TypeLits.Printf: data Rec (a :: u -> Type) (b :: [u])
- GHC.TypeLits.Printf: infixr 7 :%
- GHC.TypeLits.Printf: pattern (:%) :: () => FormatType c a => a -> FormatArgs cs -> FormatArgs (c : cs)
- GHC.TypeLits.Printf: pprintf :: forall str ps. (RPrintf str ps, RecordCurry ps) => CurriedF PP ps String
- GHC.TypeLits.Printf: pprintf_ :: forall str ps p. (RPrintf str ps, RecordCurry ps) => p str -> CurriedF PP ps String
- GHC.TypeLits.Printf: rprintf :: forall str ps. RPrintf str ps => FormatArgs ps -> String
- GHC.TypeLits.Printf: rprintf_ :: RPrintf str ps => p str -> FormatArgs ps -> String
- GHC.TypeLits.Printf: type FormatArgs = Rec PP
- GHC.TypeLits.Printf.Internal: PP :: a -> PP (c :: SChar)
- GHC.TypeLits.Printf.Internal: class RFormat (ffs :: [Either Symbol FieldFormat]) (ps :: [SChar]) | ffs -> ps
- GHC.TypeLits.Printf.Internal: class RPrintf (str :: Symbol) ps
- GHC.TypeLits.Printf.Internal: data PP (c :: SChar)
- GHC.TypeLits.Printf.Internal: instance (Data.Symbol.Utils.Listify str lst, ffs GHC.Types.~ GHC.TypeLits.Printf.Parse.ParseFmtStr_ lst, GHC.TypeLits.Printf.Internal.RFormat ffs ps) => GHC.TypeLits.Printf.Internal.RPrintf str ps
- GHC.TypeLits.Printf.Internal: instance (GHC.TypeLits.KnownSymbol str, GHC.TypeLits.Printf.Internal.RFormat ffs ps) => GHC.TypeLits.Printf.Internal.RFormat ('Data.Either.Left str : ffs) ps
- GHC.TypeLits.Printf.Internal: instance (GHC.TypeLits.Printf.Parse.Reflect ff, ff GHC.Types.~ 'GHC.TypeLits.Printf.Parse.FF f w p m c, GHC.TypeLits.Printf.Internal.FormatType c a, GHC.TypeLits.Printf.Internal.FormatFun ffs fun) => GHC.TypeLits.Printf.Internal.FormatFun ('Data.Either.Right ff : ffs) (a -> fun)
- GHC.TypeLits.Printf.Internal: instance (GHC.TypeLits.Printf.Parse.Reflect ff, ff GHC.Types.~ 'GHC.TypeLits.Printf.Parse.FF f w p m c, GHC.TypeLits.Printf.Internal.RFormat ffs ps) => GHC.TypeLits.Printf.Internal.RFormat ('Data.Either.Right ff : ffs) (c : ps)
- GHC.TypeLits.Printf.Internal: instance (a GHC.Types.~ GHC.Types.Char) => GHC.TypeLits.Printf.Internal.FormatFun '[] [a]
- GHC.TypeLits.Printf.Internal: instance GHC.TypeLits.Printf.Internal.RFormat '[] '[]
- GHC.TypeLits.Printf.Internal: rformat :: RFormat ffs ps => p ffs -> FormatArgs ps -> ShowS
- GHC.TypeLits.Printf.Internal: rprintf_ :: RPrintf str ps => p str -> FormatArgs ps -> String
- GHC.TypeLits.Printf.Internal: type FormatArgs = Rec PP
+ GHC.TypeLits.Printf.Internal: instance (a GHC.Types.~ GHC.Base.String) => GHC.TypeLits.Printf.Internal.FormatFun '[] a
+ GHC.TypeLits.Printf.Internal: instance (afun GHC.Types.~ (arg -> fun), GHC.TypeLits.Printf.Parse.Reflect ff, ff GHC.Types.~ 'GHC.TypeLits.Printf.Parse.FF f w p m c, GHC.TypeLits.Printf.Internal.FormatType c arg, GHC.TypeLits.Printf.Internal.FormatFun ffs fun) => GHC.TypeLits.Printf.Internal.FormatFun ('Data.Either.Right ff : ffs) afun
Files
- CHANGELOG.md +14/−2
- README.md +37/−58
- src/GHC/TypeLits/Printf.hs +37/−178
- src/GHC/TypeLits/Printf/Internal.hs +16/−126
- typelits-printf.cabal +2/−3
CHANGELOG.md view
@@ -1,12 +1,24 @@ Changelog ========= +Version 0.2.0.0+---------------++*February 26, 2020*++<https://github.com/mstksg/typelits-printf/releases/tag/v0.2.0.0>++* Following <https://github.com/mstksg/typelits-printf/pull/3>, we can now+ get "all three" benefits with `printf`. This version removes all traces+ of `pprintf`, `PP`, and `rprintf`, making `printf` the official+ one-size-fits-all function. Thanks @kcsongor!+ Version 0.1.1.0 --------------- *February 26, 2020* -<https://github.com/mstksg/symbols-printf/releases/tag/v0.1.1.0>+<https://github.com/mstksg/typelits-printf/releases/tag/v0.1.1.0> * Major bug fix in parser (thanks @kcsongor) @@ -15,6 +27,6 @@ *February 25, 2020* -<https://github.com/mstksg/symbols-printf/releases/tag/v0.1.0.0>+<https://github.com/mstksg/typelits-printf/releases/tag/v0.1.0.0> * Initial release
README.md view
@@ -11,84 +11,63 @@ An extensible and type-safe printf from parsing GHC TypeLits Symbol literals, matching the semantics of *[Text.Printf][]* in *base*. The difference is that-the variants here will always fail to compile if given arguments of the wrong-type (or too many or too little arguments). Most of the variants also provide-useful type feedback, telling you the type of arguments it expects and how many+your `printf`s will always fail to compile if given arguments of the wrong type+(or too many or too little arguments). It also allows you to use types to help+your development, by telling you the type of arguments it expects and how many when queried with `:t` or with typed holes. [Text.Printf]: https://hackage.haskell.org/package/base/docs/Text-Printf.html -There are three main calling conventions offered:- ```haskell ghci> putStrLn $ printf @"You have %.2f dollars, %s" 3.62 "Luigi" You have 3.62 dollars, Luigi--ghci> putStrLn $ pprintf @"You have %.2f dollars, %s" (PP 3.62) (PP "Luigi")-You have 3.62 dollars, Luigi--ghci> putStrLn $ rprintf @"You have %.2f dollars, %s" (3.62 :% "Luigi" :% RNil)-You have 3.62 dollars, Luigi ``` -Comparing their types:+Looking at its type: ```haskell-ghci> :t printf @"You have %.2f dollars, %s" 3.62 "Luigi"-FormatFun '[ .... ] fun => fun--ghci> :t pprintf @"You have %.2f dollars, %s" 3.62 "Luigi"-PP "f" -> PP "s" -> String--ghci> :t rprintf @"You have %.2f dollars, %s" 3.62 "Luigi"-FormatArgs '["f", "s"] -> String+ghci> :t printf @"You have %.2f dollars, %s"+(FormatType "f" arg1, FormatType "s" arg2)+ => arg1 -> arg2 -> String ``` -* The type of `printf` doesn't tell you immediately what you- you need. However, if you do try to use it, the type errors will guide you- along the way, iteratively.-- ```haskell- ghci> 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.+It tells you that the result is an `arg1 -> arg2 -> String`: take two+arguments, and return a `String`. The first argument must be an instance of+`FormatType "f"` (things that can be formatted by `%f`) and the second argument+must be an instance of `FormatType "s"` (things that can be formatted by `%s`). - ghci> 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.+We can see this in action by progressively applying arguments: - ghci> printf @"You have %.2f dollars, %s" 3.62 "Luigi"- You have 3.62 dollars, Luigi+```haskell+ghci> :t printf @"You have %.2f dollars, %s" 3.62+FormatType "s" arg1 => arg1 -> String - ghci> 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.- ```+ghci> :t printf @"You have %.2f dollars, %s" 3.62 "Luigi"+String+``` -* For `pprintf`, it shows you need two arguments: A `PP "f"` (which is a- value that supports being formatted by `f`) like `PP 3.62`, and a `PP "s"`,- like `PP "Luigi"`.+The type errors for forgetting to apply an argument (or applying too many+arguments) are pretty clear: -* `rprintf` tells you you need a two-item hlist (from "Data.Vinyl.Core"),- where the first item implements `f` and the second item implements `s`:- `3.62 :% "Luigi" :% RNil` will do.+```haskell+ghci> putStrLn $ 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. -The following table summarizes the features and drawbacks of each-method:+ghci> putStrLn $ 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. -| Method | True Polyarity | Naked Arguments | Type feedback |-| --------- | ---------------- | ------------------ | -------------------- |-| `printf` | Yes | Yes | Partial (via errors) |-| `pprintf` | Yes | No (requires `PP`) | Yes |-| `rprintf` | No (HList-based) | Yes | Yes |+ghci> putStrLn $ printf @"You have %.2f dollars, %s" 3.62 "Luigi"+You have 3.62 dollars, Luigi -*Ideally* we would have a solution that has all three. However, as of now, we-have a "pick two" sort of situation. Suggestions are definitely welcome,-however, if you find something that satisfies all three benefits while still-allowing for polymorphism!+ghci> putStrLn $ 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.+``` You can extend functionality with formatting for your own types by providing instances of `FormatType`.@@ -97,7 +76,7 @@ For medium-length or long strings, the parsing can be fairly slow and cause slow compile times. This might be due to the underlying mechanism that the-*[symbols][]* package exploits.+*[symbols][]* package exploits...or just GHC performance issues in general. [symbols]: https://hackage.haskell.org/package/symbols
src/GHC/TypeLits/Printf.hs view
@@ -27,102 +27,30 @@ -- -- An extensible and type-safe printf from parsing GHC TypeLits Symbol -- literals, matching the semantics of 'P.printf' from "Text.Printf" in--- /base/. The difference is that the variants here will always fail to+-- /base/. The difference is that your 'printf's will always fail to -- compile if given arguments of the wrong type (or too many or too little--- arguments). Most of the variants also provide useful type feedback,--- telling you the type of arguments it expects and how many when queried--- with @:t@ or with typed holes. See documentation in "Text.Printf" for--- details on how this formats items of various types, and the differences--- with C @printf(3)@.------ There are three main calling conventions supported:------ >>> putStrLn $ printf @"You have %.2f dollars, %s" 3.62 "Luigi"--- You have 3.62 dollars, Luigi--- >>> putStrLn $ pprintf @"You have %.2f dollars, %s" (PP 3.62) (PP "Luigi")--- You have 3.62 dollars, Luigi--- >>> putStrLn $ rprintf @"You have %.2f dollars, %s" (3.62 :% "Luigi" :% RNil)--- You have 3.62 dollars, Luigi------ Now comparing their types:------ >>> :t printf @"You have %.2f dollars, %s" 3.62 "Luigi"--- FormatFun '[ .... ] fun => fun--- >>> :t pprintf @"You have %.2f dollars, %s" 3.62 "Luigi"--- PP "f" -> PP "s" -> String--- >>> :t rprintf @"You have %.2f dollars, %s" 3.62 "Luigi"--- FormatArgs '["f", "s"] -> String------ * The type of `printf` doesn't tell you immediately what you you need.--- However, if you do try to use it, the type errors will guide you--- along the way, iteratively.------ >>> 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.------ * For 'pprintf', it shows you need two arguments: A @'PP' "f"@ (which--- is a value that supports being formatted by @f@) like @PP 3.62@, and--- a @'PP' "s"@, like @PP "Luigi"@.------ * 'rprintf' tells you you need a two-item hlist (from--- "Data.Vinyl.Core"), where the first item implements @f@ and the--- second item implements @s@: @3.62 ':%' "Luigi" :% 'RNil'@ will do.------ The following table summarizes the features and drawbacks of each--- method:------ +-----------+------------------+--------------------+----------------------+--- | Method | True Polyarity | Naked Arguments | Type feedback |--- +===========+==================+====================+======================+--- | 'printf' | Yes | Yes | Partial (via errors) |--- +-----------+------------------+--------------------+----------------------+--- | 'pprintf' | Yes | No (requires 'PP') | Yes |--- +-----------+------------------+--------------------+----------------------+--- | 'rprintf' | No (HList-based) | Yes | Yes |--- +-----------+------------------+--------------------+----------------------++-- arguments). It also allows you to use types to help your development,+-- by telling you the type of arguments it expects and how many when+-- queried with @:t@ or with typed holes. See documentation in+-- "Text.Printf" for details on how this formats items of various types,+-- and the differences with C @printf(3)@. ----- /Ideally/ we would have a solution that has all three. However, as of--- now, we have a "pick two" sort of situation. Suggestions are definitely--- welcome, however, if you find something that satisfies all three--- benefits while still allowing for polymorphism!+-- See 'printf' for the main function. ----- You can extend functionality with formatting for your own types by--- providing instances of @FormatType@.+-- You can extend functionality with formatting for your own types by providing+-- instances of 'FormatType'. -- -- Also in this module is 'pfmt', which allows you to format individual -- items according to a single format specifier.-+-- module GHC.TypeLits.Printf (- -- * Formattable things- FormatType(..)- , SChar -- * Printf- -- ** Unguarded polyarity- , printf, printf_+ printf, printf_ , PHelp, pHelp , FormatFun- -- ** Guarded polyarity- , pprintf- , pprintf_- , PP(..)- -- ** List-based polyarity- , rprintf, rprintf_- , Rec((:%), RNil), FormatArgs+ -- * Formattable things+ , FormatType(..)+ , SChar -- * Single item , pfmt , PFmt@@ -130,125 +58,56 @@ ) where import Data.Proxy-import Data.Vinyl-import Data.Vinyl.Curry import GHC.TypeLits.Printf.Internal --- | Type-safe printf with faked polyarity. Pass in a "list" of arguments--- (using ':%' and 'RNil'), instead of as multiple arguments. Call it like--- @'rprintf' \@"you have %.02f dollars, %s"@.------ >>> :t rprintf @"You have %.2f dollars, %s"--- FormatArgs '["f", "s"] -> String------ This means that it is expecting something that can be printed with @f@--- and something that can be printed with @s@. We can provide a 'Double'--- and a 'String':+-- | "Type-safe printf". Call it like @'printf' \@"you have %.02f dollars, %s"@. ----- >>> putStrLn $ rprintf @"You have %.2f dollars, %s" (3.62 ':%' "Luigi" :% 'RNil')+-- >>> putStrLn $ printf @"You have %.2f dollars, %s" 3.62 "Luigi" -- You have 3.62 dollars, Luigi ----- See 'pprintf' for a version with true polyarity and good clear types,--- but requires wrapping its arguments, and 'printf' for a version with--- true polyarity but less clear types. Also see top-level module--- documentation "GHC.TypeLits.Printf" for a more comprehensive summary.-rprintf :: forall str ps. RPrintf str ps => FormatArgs ps -> String-rprintf = rprintf_ (Proxy @str)---- | Pattern and constructor allowing you to construct a 'FormatArgs'.------ To construct a @'FormatArgs' '["f", "s"]@, for instance, you need to--- give a value formattable by @f@ and a value formattable by @s@, given--- like a linked list, with ':%' for cons and 'RNil' for nil.------ @--- 3.62 ':%' "Luigi" :% 'RNil'--- @------ (This should evoke the idea of of @3.62 : "Luigi" : []@, even though the--- latter is not possible in Haskell)-pattern (:%) :: () => FormatType c a => a -> FormatArgs cs -> FormatArgs (c ': cs)-pattern x :% xs = PP x :& xs-infixr 7 :%-{-# COMPLETE (:%) #-}---- | A version of 'pprintf' taking an explicit proxy, which allows usage--- without /TypeApplications/------ >>> :t pprintf_ (Proxy :: Proxy "You have %.2f dollars, %s")--- PP "f" -> PP "s" -> String-pprintf_ :: forall str ps p. (RPrintf str ps, RecordCurry ps) => p str -> CurriedF PP ps String-pprintf_ p = rcurry @ps (rprintf_ p)---- | Type-safe printf with true guarded polyarity. Call it like @'pprintf'--- \@"you have %.02f dollars, %s"@.------ A call to printf on a valid string will /always/ give a well-defined--- type for a function in return:------ >>> :t pprintf @"You have %.2f dollars, %s"--- PP "f" -> PP "s" -> String------ You can always query the type, and get a well-defined type back, which--- you can utilize using typed holes or other type-guided development--- techniques.+-- Looking at its type: ----- To give 'pprintf' its arguments, however, they must be wrapped in 'PP':+-- >>> :t printf @"You have %.2f dollars, %s"+-- (FormatType "f" arg1, FormatType "s" arg2)+-- => arg1 -> arg2 -> String ----- >>> putStrLn $ pprintf @"You have %.2f dollars, %s" (PP 3.62) (PP "Luigi")--- You have 3.62 dollars, Luigi+-- It tells you that the result is an @arg1 -> arg2 -> 'String'@: take two+-- arguments, and return a 'String'. The first argument must be an instance of+-- @'FormatType' "f"@ (things that can be formatted by @%f@) and the second argument+-- must be an instance of @'FormatType' "s"@ (things that can be formatted by @%s@). ----- See 'printf' for a polyariadic method that doesn't require 'PP' on its--- inputs, but with a less helpful type signature, and 'rprintf' for--- a fake-polyariadic method that doesn't require 'PP', but requires--- arguments in a single list instead. Also see top-level module--- documentation "GHC.TypeLits.Printf" for a more comprehensive summary.-pprintf :: forall str ps. (RPrintf str ps, RecordCurry ps) => CurriedF PP ps String-pprintf = pprintf_ @str @ps (Proxy @str)---- | Type-safe printf with true naked polyarity. Call it like @'printf'--- \@"you have %.02f dollars, %s"@.+-- We can see this in action by progressively applying arguments: ----- >>> putStrLn $ printf @"You have %.2f dollars, %s" 3.62 "Luigi"--- You have 3.62 dollars, Luigi+-- >>> :t printf @"You have %.2f dollars, %s" 3.62+-- FormatType "s" arg1 => arg1 -> String+-- >>> :t printf @"You have %.2f dollars, %s" 3.62 "Luigi"+-- String ----- While the type of @'printf' \@"my fmt string"@ isn't going to be very--- helpful, the error messages should help guide you along the way:+-- The type errors for forgetting to apply an argument (or applying too many+-- arguments) are pretty clear: ----- >>> printf @"You have %.2f dollars, %s"+-- >>> putStrLn $ 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+-- >>> putStrLn $ 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"+-- >>> putStrLn $ 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+-- >>> putStrLn $ 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':+-- If you want to see some useful error messages for feedback, 'pHelp' can+-- be useful: -- -- >>> 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.------ See 'pprintf' for a version of this with nicer types and type errors,--- but requires wrapping arguments, and 'rprintf' for a version of this--- with "fake" polyarity, taking a list as input instead. Also see--- top-level module documentation "GHC.TypeLits.Printf" for a more--- comprehensive summary. -- -- Note that this also supports the "interpret as an IO action to print out -- results" functionality that "Text.Printf" supports. This also supports
src/GHC/TypeLits/Printf/Internal.hs view
@@ -49,10 +49,6 @@ , Demote , Reflect(..) , FormatType(..)- , PP(..)- , RPrintf(..)- , FormatArgs- , RFormat(..) , Printf(..) , FormatFun(..) , PFmt(..)@@ -64,7 +60,6 @@ import Data.Int import Data.Proxy import Data.Symbol.Utils-import Data.Vinyl import Data.Word import GHC.OverloadedLabels import GHC.TypeLits@@ -235,141 +230,36 @@ instance FormatType "v" TL.Text where formatArg _ = P.formatArg . TL.unpack --- | Required wrapper around inputs to 'GHC.TypeLits.Printf.pprintf'--- (guarded polyarity). See documentation for--- 'GHC.TypeLits.Printf.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 @'PP' "f"@, you can use @'PP' 3.5@ or @'PP'--- 94.2@, but not @'PP' (3 :: Int)@ or @'PP' "hello"@. To make a value of--- type @'PP' c@, you must wrap a value that can be formatted via @c@.-data PP (c :: SChar) = forall a. FormatType c a => PP a---- | A heterogeneous list (from "Data.Vinyl.Core") used for calling with--- 'GHC.TypeLits.Printf.rprintf'. Instead of supplying the inputs as--- different arguments, we can gather all the inputs into a single list to--- give to 'GHC.TypeLits.Printf.rprintf'.------ >>> :t rprintf @"You have %.2f dollars, %s"--- FormatArgs '["f", "s"] -> String------ To construct a @'FormatArgs' '["f", "s"]@, you need to give a value--- formattable by @f@ and a value formattable by @s@, given like a linked--- list, with 'GHC.TypeLits.Printf.:%' 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)-type FormatArgs = Rec PP--class RFormat (ffs :: [Either Symbol FieldFormat]) (ps :: [SChar]) | ffs -> ps where- rformat :: p ffs -> FormatArgs ps -> ShowS--instance RFormat '[] '[] where- rformat _ _ = id--instance (KnownSymbol str, RFormat ffs ps) => RFormat ('Left str ': ffs) ps where- rformat _ r = showString (symbolVal (Proxy @str))- . rformat (Proxy @ffs) r--instance (Reflect ff, ff ~ 'FF f w p m c, RFormat ffs ps) => RFormat ('Right ff ': ffs) (c ': ps) where- rformat _ (PP x :& xs) = formatArg (Proxy @c) x ff- . rformat (Proxy @ffs) xs- where- ff = reflect (Proxy @ff)--class RPrintf (str :: Symbol) ps where- -- | A version of 'GHC.TypeLits.Printf.rprintf' taking an explicit- -- proxy, which allows usage without /TypeApplications/- --- -- >>> :t rprintf_ (Proxy :: Proxy "You have %.2f dollars, %s")- -- FormatArgs '["f", "s"] -> String- rprintf_ :: p str -> FormatArgs ps -> String--instance (Listify str lst, ffs ~ ParseFmtStr_ lst, RFormat ffs ps) => RPrintf str ps where- rprintf_ _ = ($ "") . rformat (Proxy @ffs)- -- | The typeclass supporting polyarity used by -- 'GHC.TypeLits.Printf.printf'. It works in mostly the same way as -- 'P.PrintfType' from "Text.Printf", and similar the same as--- 'Data.Symbol.Examples.Printf.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--- 'GHC.TypeLits.Printf.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+-- 'Data.Symbol.Examples.Printf.FormatF'. Ideally, you will never have to+-- run into this typeclass or have to deal with it directly. -- -- 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 @'IO' ()@, in the same--- way that "Text.Printf" works. We also support using strict 'T.Text'--- lazy 'TL.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.+-- chunks ('Left'). ----- If you're having problems getting the error messages to give helpful--- feedback, try using 'pHelp':+-- If you want to see some useful error messages for feedback, 'pHelp' can+-- be useful: -- -- >>> 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. class FormatFun (ffs :: [Either Symbol FieldFormat]) fun where formatFun :: p ffs -> String -> fun --- | A useful token for helping the type system give useful errors for--- 'printf':+-- | A useful tool for helping the type system give useful errors for+-- 'GHC.TypeLits.Printf.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.+-- Mostly useful if you want to force a useful type error to help see what+-- is going on. -- -- See also 'pHelp' newtype PHelp = PHelp {@@ -381,12 +271,12 @@ -- -- Either provide an argument or rewrite the format string to not expect -- -- one. --- -- Usually things would work out on their own without needing this ...- -- but sometimes the type system could need a nudge.+ -- Mostly useful if you want to force a useful type error to help see+ -- what is going on. pHelp :: String } -instance (a ~ Char) => FormatFun '[] [a] where+instance {-# INCOHERENT #-} (a ~ String) => FormatFun '[] a where formatFun _ = id instance (a ~ Char) => FormatFun '[] PHelp where formatFun _ = PHelp@@ -414,7 +304,7 @@ instance (KnownSymbol str, FormatFun ffs fun) => FormatFun ('Left str ': ffs) fun where formatFun _ str = formatFun (Proxy @ffs) (str ++ symbolVal (Proxy @str)) -instance (Reflect ff, ff ~ 'FF f w p m c, FormatType c a, FormatFun ffs fun) => FormatFun ('Right ff ': ffs) (a -> fun) where+instance {-# INCOHERENT #-} (afun ~ (arg -> fun), Reflect ff, ff ~ 'FF f w p m c, FormatType c arg, FormatFun ffs fun) => FormatFun ('Right ff ': ffs) afun where formatFun _ str x = formatFun (Proxy @ffs) (str ++ formatArg (Proxy @c) x ff "") where ff = reflect (Proxy @ff)@@ -449,7 +339,7 @@ instance (Listify str lst, ffs ~ ParseFmtStr_ lst, FormatFun ffs fun) => Printf str fun where printf_ _ = formatFun (Proxy @ffs) "" --- | Utility type powering 'pfmt'. See dcumentation for 'pfmt' for more+-- | Utility type powering 'pfmt'. See documentation for 'pfmt' for more -- information on usage. -- -- Using /OverloadedLabels/, you never need to construct this directly@@ -460,7 +350,7 @@ -- | A version of 'mkPFmt' that takes an explicit proxy input. ----- >>> pfmt (mkPFmt_ (Proxy :: Proxy ".2f") 3.6234124+-- >>> pfmt (mkPFmt_ (Proxy :: Proxy ".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)@@ -510,6 +400,6 @@ -- -- (which should be possible in GHC 8.10+) ----- Note that the format string does not include the leading @%@.+-- Note that the format string should not include the leading @%@. pfmt :: forall c a. FormatType c a => PFmt c -> a -> String pfmt (PFmt ff) x = formatArg (Proxy @c) x ff ""
typelits-printf.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: b5b3f760af5c7c80862e1246320b78e43abe2900acd93e8fe21ace4920fcc4ed+-- hash: ab8bb88b98e9aefe5c55f593c26d691a8125a6014b60204b85fda8f8ea01e90b name: typelits-printf-version: 0.1.1.0+version: 0.2.0.0 synopsis: Type-safe printf from parsing GHC TypeLits Symbol description: An extensible and type-safe printf from parsing GHC TypeLits Symbol literals, matching the semantics of 'P.printf' from "Text.Printf" in /base/.@@ -50,5 +50,4 @@ base >=4.13 && <5 , symbols >=0.3 && <0.4 , text- , vinyl >=0.12.1 default-language: Haskell2010