sayable 1.0.2.0 → 1.1.0.0
raw patch · 3 files changed
+170/−45 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Text.Sayable: SayMessage :: Doc SayableAnn -> SayMessage
- Text.Sayable: [sayMsg] :: SayMessage -> Doc SayableAnn
- Text.Sayable: newtype SayMessage
+ Text.Sayable: (&!$*) :: forall tag m t. (Sayable tag m, Foldable t) => (Doc SayableAnn -> Doc SayableAnn) -> t m -> Saying tag
+ Text.Sayable: (&!?) :: forall tag e. Sayable tag e => (Doc SayableAnn -> Doc SayableAnn) -> Maybe e -> Saying tag
+ Text.Sayable: (&<) :: forall saytag m n. (Sayable saytag m, Sayable saytag n) => m -> n -> Saying saytag
+ Text.Sayable: (&<*) :: forall saytag m n t. (Sayable saytag m, Sayable saytag n, Foldable t) => m -> t n -> Saying saytag
+ Text.Sayable: (&<?) :: forall saytag m n. (Sayable saytag m, Sayable saytag n) => m -> Maybe n -> Saying saytag
- Text.Sayable: infixl 1 &?
+ Text.Sayable: infixl 1 &!?
Files
- CHANGELOG.md +11/−0
- Text/Sayable.hs +158/−44
- sayable.cabal +1/−1
CHANGELOG.md view
@@ -1,5 +1,16 @@ # Revision history for sayable +## 1.1.0.0 -- 2023-01-17++* Added `&<`, `&<*`, and `&<?` operators to generate a newline between expressing+ their second arguments.+* Added `&!?` for Prettyprinter-processed Maybe sayable.+* Added `&!$*` for Prettyprinter-processed list result sayable.+* Removed unneeded 'SayMessage' type with 'sayMsg' extractor.+* Change `&*` and `&!*` formatting to generate spaces or newlines as appropriate+ to the current output width.+* Miscellaneous documentation fixes and enhancements+ ## 1.0.2.0 -- 2022-12-23 * Specific GHC support range for GHC 8.8--9.4
Text/Sayable.hs view
@@ -47,9 +47,9 @@ There are three messages printed: one on entry and one on either the success or failure paths. Each message may have different levels of information reported for the various arguments. -== The 'saytag' type parameter+== The @saytag@ type parameter -Each sayable message uses a 'TypeApplication' to specify a "saytag" which should+Each sayable message uses a @TypeApplication@ to specify a "saytag" which should be used for controlling the rendering of that message (e.g. "info", "verbose", "error", etc.). As a developer, it is encouraged to use whatever saytag makes sense relative to the current context and type of information being processed.@@ -81,7 +81,7 @@ The above would cause a url emitted via a "verbose" saytag to be expanded into a report on each individual field, whereas all other-saytags would simply output the 'exportURL' representation of the 'URL'.+saytags would simply output the @exportURL@ representation of the @URL@. >>> let host = Host (HTTP True) "github.com" Nothing >>> url' = URL (Absolute host) "by/one"@@ -101,13 +101,13 @@ == Operators In the logging lines above, there are three operators used, each of-which starts with the '&' character:+which starts with the @&@ character: ['&-'] This is the standard operator that takes two Sayable arguments and converts them to their Sayable form, then combining them (with an intervening space). This is the standard argument to use for building the output message from- distict parts.+ distinct parts. ['&+'] This is a variation of the standard '&-' operator that has no intervening space between the two arguments that are@@ -129,22 +129,46 @@ 'Foldable' second argument (instead of the ", " default used by the '&*' helper). - ['&?'] This is a helper operator whose second argument is a 'Maybe- a' (where 'a' is a 'Showable'). This will emit the- 'Showable' of 'a' if the argument is a 'Just' value, or+ ['&?'] This is a helper operator whose second argument is a @Maybe+ a@ (where @a@ is a @Showable@). This will emit the+ @Showable@ of @a@ if the argument is a 'Just' value, or nothing (an empty Text Showable) if the argument is a 'Nothing' value. + ['&<'] This is a helper operator that generates a newline between its two+ arguments.++ ['&<*'] This is a helper operator that combines the '&<' and '&*' operators: it+ generates a newline between its two arguments and the second argument+ is a Foldable that will be output separated by commas.++ ['&<?'] This is a helper operator that conbines the '&<' and '&?' operators: if+ the second argument is a 'Just' value, it will be output preceeded by+ the first argument and a newline. If the second argument is 'Nothing',+ only the first argument is emitted (no newline either).+ ['&!'] This is a helper operator to apply a Prettyprinter transformation function (the first argument) to a 'Sayable' message (the second argument). + ['&!?'] This helper operator is a combination of the '&!' operator and the '&?'+ operator: for a second-argument 'Just' value it will convert the value+ to a sayable and then apply the Prettyprinter conversion operator+ first-argument.+ ['&!*'] This helper operator is a combination of the '&!' operator and the '&*' operator: it applies the first argument (a @[PrettyPrinter.Doc ann] -> PrettyPrinter.Doc ann@ function) to the foldable collection represented by the second argument. + ['&!$*'] This helper operator is a combination of the '&!' operator and the+ '&*' operator: it applies the first argument (a @PrettyPrinter.Doc ann+ -> PrettyPrinter.Doc ann@ function) to the *result* of a foldable+ collection represented by the second argument. It is similar to the+ '&!*' operator except that it applies the Prettyprinter conversion to+ the singular result of the list rather than to the list of results.+ ['&!+*'] This helper operator is a combination of the '&!' operator and the '&+*' operator (and is a trinary rather than a binary operator): it applies the first argument (a @[PrettyPrinter.Doc ann] ->@@ -158,7 +182,7 @@ into a Prettyprinter.Pretty. * This module provides a helper function: 't'' which can be useful- when 'OverloadedStrings' is active to designate its argument as+ when @OverloadedStrings@ is active to designate its argument as being a Text string. If the following:@@ -172,12 +196,12 @@ can fix that: @-__ @"error" $ t'"This is an error:" &- err+saying @"error" $ t'"This is an error:" &- err @ * This module provides a helper function: 'd'' which can be useful when creating a PP.Doc SayableAnn for inclusion into a 'Sayable'- by fixing the 'ann' of 'PP.Doc ann' to 'SayableAnn'.+ by fixing the @ann@ of 'PP.Doc ann' to 'SayableAnn'. Fixes the error: @@@ -212,7 +236,7 @@ == Unfortunate Details The use of the Sayable class to translate individual objects is- fairly straightforward, but the management of the phantom 'saytag'+ fairly straightforward, but the management of the phantom @saytag@ type parameter is a bit tricky. As described above (with the Network.URL example), it's possible to provide different output generation by providing specialized instances for specific saytags.@@ -271,7 +295,7 @@ (circa 2022).] There's another twist to this story though. To observe this new- twist, add a 'Baz' datastructure and its generic 'Sayable' instance:+ twist, add a @Baz@ datastructure and its generic 'Sayable' instance: > data Baz = Baz Foo > instance Sayable saytag Baz where sayable (Baz a) = t'"BAZ :=" &- foo@@ -291,15 +315,15 @@ generic 'Sayable' instance has a constraint for the inner element, which causes GHC to wait until the final use case to determine what the specific type parameters are; it sees the @"loud"@ @saytag@ value- and selects the @"loud"@ 'Foo' 'Sayable' instance as the most specific.+ and selects the @"loud"@ @Foo@ 'Sayable' instance as the most specific. However, the @baz@ 'Sayable' instance does not have a constraint, so GHC takes the conservative approach and uses the most general instance,- which means that it transitively selects the generic 'Foo' 'Sayable'+ which means that it transitively selects the generic @Foo@ 'Sayable' instance instead of the @"loud"@ instance. There are two ways to fix this: - 1. Provide explicit @"loud"@ 'Sayable' instance for 'Baz'. This is+ 1. Provide explicit @"loud"@ 'Sayable' instance for @Baz@. This is problematic, because this must be done for *each* saytag for which there is a variation and it must be done for *each* upper level 'Sayable' instance.@@ -309,9 +333,9 @@ it makes no assumptions about current or future saytags and variations. - This 'Sayable' constraint was already present on the 'Bar'- 'Sayable' instance because of the parameterized type for 'Bar';- the 'Baz' type has no type parameter, but a constraint can still+ This 'Sayable' constraint was already present on the @Bar@+ 'Sayable' instance because of the parameterized type for @Bar@;+ the @Baz@ type has no type parameter, but a constraint can still be added for each interior type: > instance Sayable saytag Foo => Sayable saytag Baz where@@ -351,9 +375,14 @@ module Text.Sayable (+ -- * Primary Class Sayable(sayable)+ -- * Result Datatype , Saying(Saying, saying)- , SayMessage(SayMessage, sayMsg)+ -- * Helper operators+ --+ -- | These are typically used to facilitate the expression of a sayable+ -- phrase containing multiple elements. , t' , d' , (&-)@@ -362,10 +391,23 @@ , (&*) , (&+*) , (&?)+ , (&<)+ , (&<*)+ , (&<?) , (&!)+ , (&!?) , (&!*)+ , (&!$*) , (&!+*)+ -- * Annotation used in Sayables+ --+ -- | Generating a 'Prettyprinter.Doc' requires the identification of an @ann@+ -- type parameter. For 'Sayable', this type parameter is the 'SayableAnn',+ -- although the 'Prettyprinter.reAnnotate' function can be used to switch+ -- from this abstract annotation to a functional annotation+ -- (e.g. 'Prettyprinter.Render.Terminal.AnsiStyle') , SayableAnn(SayableAnn)+ -- * Simple String Extraction , sez ) where@@ -387,7 +429,7 @@ import qualified Prettyprinter as PP --- | The main class of things that can be passed to 'say'. Arguments+-- | The main class of things that can be passed to 'sayable'. Arguments -- provided to 'sayable' or 'sez' will be converted to the sayable form by -- automatically applying the appropriate instance of this class. The -- default implementation is:@@ -413,12 +455,7 @@ instance Semigroup (Saying tag) where Saying sm1 <> Saying sm2 = Saying $ sm1 <+> sm2 --- | This is the principle data type that carries 'say' messages to the output--- conversion point. This is generated internally by the 'saying' functions and--- is usually not directly needed by client code. -newtype SayMessage = SayMessage { sayMsg :: PP.Doc SayableAnn }- -- | Inputs that are 'Sayable', i.e. that can be converted to a Saying instance {-# OVERLAPPING #-} (tagA ~ tagB) => Sayable tagA (Saying tagB) where sayable = id@@ -449,15 +486,15 @@ -- For example: -- -- > instance Sayable saytag Foo where--- > sayable foo = sayable @saytag $ PP.group $ PP.pretty+-- > sayable foo = sayable @saytag $ Prettyprinter.group $ Prettyprinter.pretty -- > $ field1 foo &- sayable @saytag PP.line &- field2 foo ----- This uses Prettyprinter's 'group' and 'line' formatters to show the--- two fields on the same line if they will fit, otherwise stacked on--- top of each other. Note that the second portion needs an explicit--- 'TypeApplication' (applied here to the 'PP.line') because the--- 'PP.group' and 'PP.pretty' functions do not propagate that outer--- 'saytag' to the inner portion.+-- This uses 'Prettyprinter.group' and 'Prettyprinter.line' formatters to show+-- the two fields on the same line if they will fit, otherwise stacked on top of+-- each other. Note that the second portion needs an explicit @TypeApplication@+-- (applied here to the 'Prettyprinter.line') because the 'Prettyprinter.group'+-- and 'Prettyprinter.pretty' functions do not propagate that outer @saytag@ to+-- the inner portion. instance PP.Pretty (Saying tag) where pretty = PP.unAnnotate . saying @@ -508,14 +545,14 @@ -- -- Note: this instance makes it easy to output lists, Sequence, -- NonEmpty.List, etc., but it can have undesireable effects for data--- tructures whose Foldable (Functor) is irregular... for example,+-- structures whose Foldable (Functor) is irregular... for example, -- folding over a tuple only returns the 'snd' value of a tuple. -- Consider wrapping tuples in a newtype with an explicit Sayable to -- avoid this. (&*) :: forall tag m e t . (Sayable tag m, Sayable tag e, Foldable t) => m -> t e -> Saying tag m &* l = let addElem e (s, Saying p) =- (", ", Saying $ saying (sayable @tag e) <> s <> p)+ ("," <> PP.softline, Saying $ saying (sayable @tag e) <> s <> p) in sayable m <> (snd $ foldr addElem ("", Saying PP.emptyDoc) l) infixl 1 &* @@ -533,19 +570,40 @@ infixl 2 &+* --- | A helper operator that applies the first argument which converts--- an array of 'Prettyprinter.Doc ann' elements to a single--- 'PrettyPrinter.Doc ann' element to the second argument, which is a--- Foldable collection of 'Sayable' items. This is essentially a--- combination of the '&!' and '&*' operators.+-- | A helper operator that applies the first argument which converts an array of+-- 'Prettyprinter.Doc ann' elements to a single 'PrettyPrinter.Doc ann' element+-- to the second argument, which is a Foldable collection of 'Sayable' items.+-- This is essentially a combination of the '&!' and '&*' operators where the+-- first operation takes the list of doc items and returns a single item.+--+-- > import qualified Prettyprinter as PP+-- >+-- > putStrLn $ sez @"info" $ t'"The stooges are" &- PP.hsep &!* ["Larry", "Mo", "Curly"]+-- The stooges are Larry Mo Curly+-- (&!*) :: forall tag m t . (Sayable tag m, Foldable t) => ([PP.Doc SayableAnn] -> PP.Doc SayableAnn) -> t m -> Saying tag-pf &!* l = let addElem e (s, p) = (", ", saying (sayable @tag e) <> s : p)+pf &!* l = let addElem e (s, p) = ("," <> PP.softline+ , saying (sayable @tag e) <> s : p) in Saying $ pf $ snd $ foldr addElem ("", []) l infixl 2 &!* +-- | A helper operator that applies the first argument (a Prettyprinter+-- adaptation function) to the result of a Foldable collection of 'Sayable'+-- items. This is essentially a combination of the '&!' and '&*' operators where+-- the first operation is applied to the entire list, rather than each element of+-- the list (as with `&!*`).+(&!$*) :: forall tag m t+ . (Sayable tag m, Foldable t)+ => (PP.Doc SayableAnn -> PP.Doc SayableAnn) -> t m -> Saying tag+pf &!$* l = let addElem e (s, p) = ("," <> PP.softline+ , saying (sayable @tag e) <> s <> p)+ in Saying $ pf $ snd $ foldr addElem ("", mempty) l+infixl 2 &!$*++ -- | A helper operator that applies the first argument which converts -- an array of 'Prettyprinter.Doc ann' elements to a single -- 'PrettyPrinter.Doc ann' element to the second argument, which is a@@ -585,7 +643,56 @@ m &? (Just a) = sayable m <> sayable a infixl 1 &? --- | A helper function to use when 'OverloadedStrings' is active to+-- | A helper operator allowing a Sayable item to be wrapped in a 'Maybe' and a+-- prettyprinter conversion as the first argument. This is a combination of the+-- `&!` and `&?` operators.+(&!?) :: forall tag e . (Sayable tag e)+ => (PP.Doc SayableAnn -> PP.Doc SayableAnn) -> Maybe e -> Saying tag+_ &!? Nothing = Saying mempty+pf &!? (Just a) = Saying $ pf $ saying $ sayable @tag a+infixl 1 &!?++-- | A helper operator that generates a newline between its two arguments. Many+-- times the '&-' operator is a better choice to allow normal prettyprinter+-- layout capabilities, but in situations where it is known that multiple lines+-- will or should be generated, this operator makes it easy to separate the+-- lines.+(&<) :: forall saytag m n . (Sayable saytag m, Sayable saytag n)+ => m -> n -> Saying saytag+m &< n = Saying+ $ (saying $ sayable @saytag m)+ <> (PP.line :: PP.Doc SayableAnn)+ <> (saying $ sayable @saytag n)+infixl 1 &<++-- | A helper operator that combines '&<' and '&*' which will generate a newline+-- between its two arguments, where the second argument is a foldable collection+-- whose elements will be sayable emitted with comma separators.+(&<*) :: forall saytag m n t . (Sayable saytag m, Sayable saytag n, Foldable t)+ => m -> t n -> Saying saytag+m &<* n = let addElem e (s, Saying p) =+ (", ", Saying $ saying (sayable @saytag e) <> s <> p)+ in Saying+ $ (saying $ sayable @saytag m)+ <> (PP.line :: PP.Doc SayableAnn)+ <> (saying $ sayable @saytag+ (snd $ foldr addElem ("", Saying PP.emptyDoc) n))+infixl 1 &<*++-- | A helper operator that emits the first argument and optionally emits a+-- newline and the 'Just' value of the second argument if the second argument is+-- not 'Nothing'+(&<?) :: forall saytag m n . (Sayable saytag m, Sayable saytag n)+ => m -> Maybe n -> Saying saytag+m &<? Nothing = sayable m+m &<? (Just n) = Saying+ $ (saying $ sayable @saytag m)+ <> (PP.line :: PP.Doc SayableAnn)+ <> (saying $ sayable @saytag n)+infixl 1 &<?+++-- | A helper function to use when @OverloadedStrings@ is active to -- identify the following quoted literal as a "Data.Text" object. t' :: Text -> Text t' = id@@ -593,7 +700,7 @@ -- | A helper function to use when creating a PP.Doc SayableAnn data--- object (i.e. fixing the 'ann' of 'Doc ann' to 'SayableAnn')+-- object (i.e. fixing the @ann@ of 'Doc ann' to 'SayableAnn') d' :: PP.Pretty n => n -> PP.Doc SayableAnn d' = PP.pretty @@ -625,7 +732,14 @@ ---------------------------------------------------------------------- -- | This is a convenience function that can be used for simple conversions of a--- Sayable to a String.+-- Sayable to a String. The use of this function is not generally recommended: a+-- more controlled rendering of the resulting 'Prettyprinter.Doc' (obtained from+-- the via 'saying') is recommended, but there are times (especially when+-- debugging) when a quick conversion/extraction to a @String@ is convenient.+--+-- This function is often used with a type application:+--+-- > putStrLn $ sez @"info" $ "There are" &- length lst &- "list elements." sez :: forall saytag a . Sayable saytag a => a -> String sez = show . saying . sayable @saytag
sayable.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: sayable-version: 1.0.2.0+version: 1.1.0.0 synopsis: Data structures, classes and operators for constructing context-adjusted pretty output description: .