-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Construction of context-adjusted pretty output -- -- This package provides a set of data structures, classes and operators -- that facilitate the construction of a Prettyprinter Doc object. The -- difference between this an Prettyprinter is: -- --
-- import qualified Prettyprinter as PP
--
-- foo :: Members '[ Logging SayMessage, Config ] r -> a -> b -> Eff r [b]
-- foo arg1 arg2 =
-- do putStrLn $ show $ saying $ sayable info "Entering foo with" &- arg1 &- "and" &- arg2
-- rslt <- something arg1 arg2
-- case rslt of
-- Right vals ->
-- do putStrLn $ show $ saying $ sayable "verbose"
-- $ "Foo successfully returning" &% length vals &- "results:" &- vals
-- return vals
-- Left err ->
-- do putStrLn $ show $ saying $ sayable @"error"
-- $ "Foo error (" &- arg1 &- PP.comma &- arg2 &- ") is" &- err
-- throwError err
--
--
-- [Note: if viewing via Haddock HTML, the ampersand in front of
-- "info", "verbose", and "error" on the
-- putStrLn lines above may not be visible.]
--
-- 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.
--
--
-- import Network.URL
--
-- instance Sayable "verbose" URL where
-- sayable url =
-- let newline = PP.line :: PP.Doc SayableAnn
-- prettyShow x = PP.viaShow x :: PP.Doc SayableAnn
-- in "URL {"
-- &- "url_type=" &- prettyShow (url_type url) &- newline
-- &- "url_path=" &- url_path url &- newline
-- &- "url_params=" &* url_params url
-- &- "}"
-- instance Sayable saytag URL where
-- sayable = Sayable . PP.viaShow . exportURL
--
--
-- 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.
--
--
-- >>> let host = Host (HTTP True) "github.com" Nothing
--
-- >>> url' = URL (Absolute host) "by/one"
--
-- >>> saying $ sayable @"verbose" url'
-- URL { url_type= Absolute (Host {protocol = HTTP True, host= "github.com", port= Nothing})
-- url_path= by/one
-- url_params= }
--
-- >>> saying @"info" $ sayable url'
-- https://github.com:442/by/one
--
--
-- There are some tricky elements to the above however; see "Unfortunate
-- Details" below.
--
-- Note that there are several pre-declared Sayable instances for common
-- datatypes for convenience.
--
-- -- saying @"error" $ "This is an error:" &- err ---- -- results in an error Could not deduce (Data.String.IsString m0) -- arising from the literal '"This is an error:"' then this helper -- can fix that: -- --
-- saying @"error" $ t'"This is an error:" &- err ---- --
-- • Overlapping instances for Sayable saytag (PP.Doc ann1) -- arising from a use of ‘&-’ -- Matching instances: -- instance [overlappable] Sayable tag (PP.Doc ann) -- -- Defined in ‘Taphos.Say’ -- instance Sayable tag (PP.Doc SayableAnn) -- -- Defined in ‘Taphos.Say’ -- (The choice depends on the instantiation of ‘saytag, ann1’ -- To pick the first instance above, use IncoherentInstances -- when compiling the other instance declarations) ---- -- This is similar to the &% operator except it takes a single -- argument rather than the two arguments passed to the operator. -- --
-- import Network.URL ( URL )
-- newtype Foo = Foo URL
-- data Bar a = Bar String a
--
-- -- [previous instances for Sayable URL here...]
--
-- instance Sayable "loud" Foo where sayable (Foo url) = t'"{!" &- url &- t'"!}"
-- instance Sayable saytag Foo where sayable (Foo url) = sayable url
--
-- instance (Sayable saytag a) => Sayable saytag (Bar a) where
-- sayable (Bar b a) = b &- t'"is" &- a
--
-- let host = Host (HTTP True) "github.com" Nothing
-- let url' = URL (Absolute host) "by/one"
-- let foo = Foo url'
-- let bar = Bar "bar" foo
--
--
-- will generate:
--
--
-- >>> putStrLn $ sez @"info" $ t'"INFO:" &- bar &- "via" &- foo
-- INFO: bar is "https://github.com/by/one" via "https://github.com/by/one"
--
-- >>> putStrLn $ sez @"loud" $ t'"LOUD:" &- bar &- "via" &- foo
-- LOUD: bar is {! "https://github.com/by/one" !} via {! "https://github.com/by/one" !}
--
--
-- which is expected. However, if the calls to sez are moved to a
-- separate file from the instance declarations, the compilation error
-- will be:
--
-- -- Overlapping instances for Sayable "loud" Foo arising from a use of &- ---- -- for the last (loud) line. To resolve this, use OVERLAPPING and/or -- OVERLAPPABLE specifications on the instance declarations. Usually it's -- sufficient (and easiest) to add the OVERLAPPABLE to the generic -- instance: -- --
-- instance Sayable "loud" Foo where sayable (Foo s) = t'"{!" &- s &- t'"!}"
-- instance {-# OVERLAPPABLE #-} Sayable saytag Foo where sayable (Foo s) = sayable s
--
--
-- [Note: if you are viewing the above via Haddock HTML, the second line
-- has the instance keyword, followed by an open comment
-- directive (open curly brace, dash, hash) , OVERLAPPABLE and a
-- closing comment directive (hash, dash, close curly brace), followed by
-- the Sayable keyword, but that doesn't render under HTML
-- Haddock (circa 2022).]
--
-- -- data Baz = Baz Foo -- instance Sayable saytag Baz where sayable (Baz a) = t'"BAZ :=" &- foo ---- -- Now the following calls and corresponding output can be observed: -- --
-- >>> putStrLn $ sez @"info" $ t'"INFO:" &- bar &- t'"and" &- baz
-- INFO: bar is "https://github.com/by/one" and BAZ := "https://github.com/by/one"
--
-- >>> putStrLn $ sez @"loud" $ t'"LOUD:" &- bar &- t'"and" &- baz
-- LOUD: bar is {! "https://github.com/by/one" !} and BAZ := "https://github.com/by/one"
--
--
-- Notice how the foo value in bar changes when the
-- '"loud"' saytag is used, but the same foo value in
-- baz@ does not change!
--
-- The difference here is in the mechanism GHC uses to select instances
-- (as described on the referenced link above). In short, for
-- bar, the 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. 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 instance instead of the "loud"
-- instance.
--
-- There are two ways to fix this:
--
-- -- instance Sayable saytag Foo => Sayable saytag Baz where -- sayable (Baz a) = t'"BAZ :=" &- foo ---- -- To facilitate generating the needed set of constraints for -- sub-elements (including ensuring that a sub-element isn't missed when -- writing these by hand), there is a Template Haskell helper that will -- automatically generate these constraints: -- --
-- instance $(sayableSubConstraints (const True) ''Baz "tag" []) => Sayable tag Baz where ... ---- -- See the sayableSubConstraints documentation for more -- information on using this Template Haskell helper. -- -- Using either of the above solutions, the new output is fully -- specialized as desired: -- --
-- >>> putStrLn $ sez @"info" $ t'"INFO:" &- bar &- t'"and" &- baz
-- INFO: bar is "https://github.com/by/one" and BAZ := "https://github.com/by/one"
--
-- >>> putStrLn $ sez @"loud" $ t'"LOUD:" &- bar &- t'"and" &- baz
-- LOUD: bar is {! "https://github.com/by/one" !} and BAZ := {! "https://github.com/by/one" !}
--
module Text.Sayable
-- | 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:
--
-- -- sayable = Saying . Prettyprinter.pretty --class Sayable (tag :: Symbol) v sayable :: Sayable tag v => v -> Saying tag sayable :: (Sayable tag v, Pretty v) => v -> Saying tag -- | The result of applying the sayable method of the Sayable class is the -- Saying object. This object is internal to the Say module and is mostly -- used for subsequently combining with additional Saying objects to -- produce the final Saying object that is converted to a SayMessage for -- actual logging. A Sayable supports a Semigroup combinator to allow -- composition of messages. newtype Saying (tag :: Symbol) Saying :: Doc SayableAnn -> Saying (tag :: Symbol) [saying] :: Saying (tag :: Symbol) -> Doc SayableAnn -- | A helper function to use when OverloadedStrings is active to -- identify the following quoted literal as a Data.Text object. It -- is common to enable OverloadedStrings because Pretty declares -- an IsString instance and thus facilitates the pretty-printing -- of string values, but this causes GHC to emit warnings about assuming -- the types of strings, so this function can be used to clarify the -- intended type. -- --
-- >>> putStrLn $ t'"This is type: Data.Text" -- "This is type: Data.Text" --t' :: Text -> Text -- | A helper function to use when creating a PP.Doc SayableAnn data object -- (i.e. fixing the ann of 'Doc ann' to SayableAnn) d' :: Pretty n => n -> Doc SayableAnn -- | A helper operator allowing two Sayable items to be composed into a -- Saying. This is the most common operator used to construct composite -- Sayable messages. The two Sayable items are separated by a space. -- --
-- >>> sez @"info" $ t'"hello" &- t'"world" -- "hello world" --(&-) :: forall saytag m n. (Sayable saytag m, Sayable saytag n) => m -> n -> Saying saytag infixl 1 &- -- | A helper operator allowing two Sayable items to be composed into a -- Saying by placing the two Sayable items immediately adjacent with no -- intervening spaces. This is the high-density version of the more -- common &- operator. -- --
-- >>> sez @"info" $ t'"hello" &+ t'"world" -- "helloworld" --(&+) :: forall saytag m n. (Sayable saytag m, Sayable saytag n) => m -> n -> Saying saytag infixl 1 &+ -- | A helper operator allowing a Sayable item to be composed with a Pretty -- item into a Saying. This is infrequently used and primarily allows the -- composition of a data object which has a Prettyprinter instance -- but no Sayable instance. -- --
-- >>> sez @"info" $ t'"hello" &% (t'"world", t'"!") -- "hello (world, !)" --(&%) :: (Sayable tag m, Pretty n) => m -> n -> Saying tag infixl 1 &% -- | A helper operator allowing a Sayable item to be composed with a -- Foldable series of Sayable items. This can be used when the second -- argument is a List, Sequence, Set, etc. to add all elements of the set -- (comma-separated). -- -- Note: this instance makes it easy to output lists, Sequence, -- NonEmpty.List, etc., but it can have undesireable effects for data -- 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. -- --
-- >>> sez @"info" $ t'"three:" &* [1, 2, 3::Int] -- "three: 1, 2, 3" ---- -- If the second argument is a null collection then no output is -- generated for it. (&*) :: forall tag m e t. (Sayable tag m, Sayable tag e, Foldable t) => m -> t e -> Saying tag infixl 1 &* -- | A helper operator that generates a sayable from a foldable group (e.g. -- list) of sayable items. This helper is linke the &* -- operator except that the folded output is immediately adjacent to the -- preceeding sayable output instead of separated by a space; this is -- useful for situations where the folded output has delimiters like -- parentheses or brackets. -- --
-- >>> sez @"info" $ t'"three:" &- '(' &+* [1,2,3::Int] &+ ')'
-- "three: (1, 2, 3)"
--
--
-- If the second argument is an empty collection then no output is
-- generated for it.
(&+*) :: forall tag m e t. (Sayable tag m, Sayable tag e, Foldable t) => m -> t e -> Saying tag
infixl 1 &+*
-- | A helper operator that generates a sayable from a list of sayable
-- items, separated by the first sayable argument (instead of the ", "
-- that use used by the &* operator).
--
-- -- >>> sez @"info" $ t'"three:" &- t'".." &:* [1, 2, 3::Int] -- "three: 1..2..3" --(&:*) :: forall tag m e t. (Sayable tag m, Sayable tag e, Foldable t) => m -> t e -> Saying tag infixl 2 &:* -- | A helper operator allowing a Sayable item to be wrapped in a -- Maybe. This adds the Sayable of the first argument to -- the Sayable of the second argument in the Just case, or -- just emits the Sayable of the first argument if the second -- argument is Nothing. -- --
-- >>> sez @"info" $ t'"It's" &? Just (t'"something") &- t'"or" &? (Nothing :: Maybe Text) -- "It's something or" --(&?) :: forall tag m e. (Sayable tag m, Sayable tag e) => m -> Maybe e -> Saying tag infixl 1 &? -- | A helper operator that emits the first argument and optionally emits a -- the Just value of the second argument immediately thereafter if -- the second argument is not Nothing -- --
-- >>> sez @"info" $ t'"It's" &+? Nothing &- t'"ok" &+? Just "time" -- "It's oktime" ---- -- @since: 1.2.0.0 (&+?) :: forall saytag m n. (Sayable saytag m, Sayable saytag n) => m -> Maybe n -> Saying saytag 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. -- --
-- >>> sez @"info" $ t'"Hello" &< t'"world" -- "Hello\nworld" ---- -- @since: 1.1.0.0 (&<) :: forall saytag m n. (Sayable saytag m, Sayable saytag n) => m -> n -> Saying saytag 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. -- --
-- >>> sez @"info" $ t'"three:" &<* [1, 2, 3::Int] -- "three:\n1, 2, 3" ---- -- @since: 1.1.0.0 (&<*) :: forall saytag m n t. (Sayable saytag m, Sayable saytag n, Foldable t) => m -> t n -> Saying saytag 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 (a combination of the &< -- and &? operators). -- --
-- >>> sez @"info" $ t'"First" &<? Just (t'"something") -- "First\nsomething" -- -- >>> sez @"info" $ t'"Then" &<? (Nothing :: Maybe Text) -- "Then" ---- -- @since: 1.1.0.0 (&) :: forall saytag m n. (Sayable saytag m, Sayable saytag n) => m -> Maybe n -> Saying saytag infixl 1 & -- | A helper operator to apply a Prettyprinter (Doc ann -- -> Doc ann) function (the first argument) to the Sayable in -- the second argument. This is different from the &% operator -- in that the former uses hcat to join two independent Doc -- Saying values, whereas this operator applies a transformation -- (e.g. Prettyprinter.annotate AnnValue or -- Prettyprinter.align . Prettyprinter.group) to the Doc -- in the second Saying argument. -- --
-- >>> sez @"info" $ PP.group &! t'"hi" -- "hi" --(&!) :: forall tag m. Sayable tag m => (Doc SayableAnn -> Doc SayableAnn) -> m -> Saying tag infixl 2 &! -- | 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. -- --
-- >>> sez @"info" $ PP.group &!? Just (t'"hi") -- "hi" ---- -- @since: 1.1.0.0 (&!?) :: forall tag e. Sayable tag e => (Doc SayableAnn -> Doc SayableAnn) -> Maybe e -> Saying tag infixl 1 &!? -- | A helper operator that is a combination of the &! and -- &* operators. It applies the first argument (which converts -- an array of 'Prettyprinter.Doc ann' elements into a single -- 'PrettyPrinter.Doc ann' element) to the second argument (which is a -- Foldable collection of Sayable items). -- --
-- >>> sez @"info" $ t'"three:" &- PP.align . PP.vsep &!* [1, 2, 3::Int] -- "three: 1, \n 2, \n 3" --(&!*) :: forall tag m t. (Sayable tag m, Foldable t) => ([Doc SayableAnn] -> Doc SayableAnn) -> t m -> Saying tag 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 &!*). -- --
-- >>> sez @"info" $ t'"three:" &- PP.align &!$* [1, 2, 3::Int] -- "three: 1, 2, 3" ---- -- As with the &!* operator (and unlike the &* -- operator), a null collection is passed to the converter first -- argument. -- -- @since: 1.1.0.0 (&!$*) :: forall tag m t. (Sayable tag m, Foldable t) => (Doc SayableAnn -> Doc SayableAnn) -> t m -> Saying tag 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. -- -- Unlike the other operators defined in this package, this is a trinary -- operator rather than a binary operator. Because function application -- (whitespace) is the highest precedence, the last argument will -- typically need a preceeding $ to prevent applying the second argument -- to the third argument before applying this operator. -- --
-- >>> sez @"info" $ t'"three:" &- (PP.align . PP.vsep &!:* (t'" or")) [1, 2, 3::Int] -- "three: 1 or\n 2 or\n 3" --(&!:*) :: forall tag m t b. (Sayable tag b, Sayable tag m, Foldable t) => ([Doc SayableAnn] -> Doc SayableAnn) -> b -> t m -> Saying tag infixl 2 &!:* -- | This is the default annotation type for the Saying module. The -- Prettyprinter reannotate operation can be used to change this -- annotation into any other annotation type the client desires. -- -- The SayableAnn is an instance of IsLabel, so if OverloadedLabels is -- enabled, this can easily be specified: -- --
-- import qualified Prettyprinter as PP -- import Text.Sayable -- -- putStrLn $ sez @"info" $ PP.annotate #myann $ Hello &- "world!" ---- -- Note however that labels cannot start with a capital letter. data SayableAnn SayableAnn :: Text -> SayableAnn -- | This is a convenience function that can be used for simple conversions -- of a Sayable to a String. The use of this function is not generally -- recommended: a more controlled rendering of the resulting 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." ---- -- Note that this will use the show representation provided by -- Prettyprinter; notably this will usually assume a width of 80 -- characters and perform wrapping accordingly. sez :: forall saytag a. Sayable saytag a => a -> String -- | This is a convenience function similar to the sez helper, but -- specifies an unlimited width so there is no wrapping. sez_ :: forall saytag a. Sayable saytag a => a -> String -- | When creating Sayable instances, it is necessary to create a -- Sayable constraint for all sub-element data structures so that GHC -- will search for the specific tagged instance, otherwise GHC will use a -- default instance that is not necessarily associated with the current -- tag (see the "Sub-Element Constraints" section above for more -- information). -- -- The sayableSubConstraints function is a Template Haskell helper -- that can automatically generate the constraints for the sub-elements -- of this datastructure. This has several advantages, including brevity -- and ensuring that no sub-elements are missed. The -- sayableSubConstraints is a function taking a ConstrM -- monad where that monad specifies the various information (via the -- ConstrM operations defined below) that are needed to generate -- the sub-element constraints. -- -- To use this, you will need to enable the ConstraintKinds and -- TemplateHaskell pragmas. -- -- With these enabled, the instance declaration would be specified as: -- --
-- instance $(sayableSubConstraints $ ofType ''Baz) => Sayable saytag Foo where ... ---- -- If there are other constraints that should also be included, those can -- be specified in a standard constraint tuple: -- --
-- instance ( $(sayableSubConstraints $ ofType ''Baz) -- , Show Bar -- ) => Sayable tag Foo where ... ---- -- The sayableSubConstraints function will examine the definition -- of the type referenced by the second argument, and for every sub-type -- referenced (that is accepted by any subElemFilter specified), -- it will generate a Sayable constraint for the sub type(s). -- --
-- data Bar = ...
-- data Baz = ...
-- data Foo = Foo { fld1 :: Bar
-- , fld2 :: [Baz]
-- , fld3 :: Maybe Bar
-- }
--
-- instance $(sayableSubConstraints $ do ofType ''Foo
-- tagVar "stag"
-- ) => Sayable stag Foo where
-- sayable foo = ...
--
--
-- becomes (via the magic of Template Haskell):
--
-- -- instance ( Sayable stag Bar -- , Sayable stag Baz -- ) => Sayable stag Foo where -- sayable foo = ... ---- -- The subElemFilter ConstrM operation can be used to -- select only a subset of the sub-elements for this constraints -- generation. For example, with the following definition: -- --
-- data Foo2 = FC1 Bar [Maybe Baz] | FC2 Bar Int HiddenValue ---- -- The Sayable instance for Foo2 does not need a tag-specific constraint -- for the Int type, and the instance will not output the fld5 -- hidden value, so no Sayable instance constraint is needed (and -- in fact, for safety, no actual Sayable instance will ever be -- created for HiddenValue). To support this, a filtering -- function can be used for the Language.Haskell.TH.Name type -- references: -- --
-- module MyModule where
--
-- import qualified Language.Haskell.TH as TH
--
-- data Bar = ...
-- data Baz = ...
-- data HiddenValue = ...
-- data Foo2 = FC1 Bar [Baz] | FC2 (Maybe Bar) Int HiddenValue
--
-- foo2Filter :: TH.Name -> Bool
-- foo2Filter nm = and [ "HiddenValue" /= TH.nameBase nm
-- , maybe False ("MyModule" ==) $ TH.nameModule nm
-- ]
--
-- instance $(sayableSubConstraints $ do ofType ''Foo2
-- tagVar "t"
-- subElemFilter foo2Filter
-- ) => Sayable t Foo2 where
-- sayable = case
-- FC1 x y -> "First Foo2 form with" &- x &- "and" &- y
-- FC2 x y h -> "Second Foo2 form with" &? x &- y
--
-- instance Sayable t Bar where ...
-- instance Sayable t Baz where ...
--
--
-- which generates:
--
-- -- instance ( Sayable t Bar, Sayable t Baz ) => Sayable t Foo2 where ... ---- -- When the sub-elements are themselves parameterized, it is necessary to -- specify what those parameters should map to: either the same parameter -- variables that occur on the main type, or other types or values based -- on the usage of the main type. This can be done using the -- paramVar, paramSym, paramNat, or paramTH -- ConstrM operations. Each of these param declarations specifies -- one of the parameters for sub-elements. All sub-elements must specify -- the parameters in the same order, although each does not need to use -- all of the parameters; sub-element types that do not follow this -- arrangement will need to be manually specified instead (and filtered -- out of the template-haskell generated constraints via the -- subElemFilter). -- --
-- data Bar a = Bar (Maybe a)
-- data Baz a b = BazL a | BazR b
--
-- data Foo3 a = Foo3 { inputs :: Bar a, outputs :: Baz a String }
--
-- instance $(sayableSubConstraints $ do ofType ''Foo3
-- tagVar "t"
-- paramVar "a"
-- paramTH $ TH.ConT ''String
-- ) => Sayable t (Foo3 a) where ...
--
--
-- generates the following:
--
-- -- instance ( Sayable t (Bar a), Sayable t (Baz a String), Sayable t a ) => Sayable (Foo3 a) where ... ---- -- Clearly, there are some limitations to the -- sayableSubConstraints capabilities, so it is not always useable -- in all situations (e.g. with tuples). The sub-element constraints can -- and should be manually generated in these situations. -- -- In order to debug the output of the sayableSubConstraints or -- otherwise examine its suitability, enable -ddump-splices when -- compiling. sayableSubConstraints :: ConstrM () -> PredQ -- | ConstrM is a monadic context for describing the constraint parameters -- needed by sayableSubConstraints when generating sub-element -- Sayable constraints. data ConstrM a -- | This ConstrM operation is used to declare the target data type for -- which the Sayable constraints are to be generated via -- template-haskell. -- --
-- instance $(sayableSubConstraints $ do { ofType ''Foo; ... }) => Sayable t Foo where
--
--
-- If not used, the default type is (), which is not likely to
-- be the desired type.
ofType :: Name -> ConstrM ()
-- | This ConstrM operation is used to declare the name of the saytag
-- variable used to specify the "Saying VAR sub-element" constraints.
-- This should match the variable for the primary instance declaration.
--
--
-- instance $(sayableSubConstraints $ do { ...; tagVar "t" }) => Sayable t X where ...
--
--
-- If not used, the default saytag is the variable saytag. This
-- operation is coincident with the tagSym, tagNat, and
-- tagTH operations and the last such operation will be the one
-- used.
tagVar :: String -> ConstrM ()
-- | This ConstrM operation is used to declare a Symbol singleton value for
-- the saytag of the sub-element Sayable constraints. This should match
-- the Symbol used for the primary instance declaration.
--
-- -- instance $(sayableSubConstraints $ do tagSym "loud" -- ofType ''X -- ) => Sayable "loud" X where ... ---- -- If not used, the default saytag is the variable saytag. This -- operation is coincident with the tagVar, tagNat, and -- tagTH operations and the last such operation will be the one -- used. tagSym :: String -> ConstrM () -- | Sometimes the sub-constraints should be wrapped in another type (via a -- constructor for that type). For example: -- -- @ data WithIndentation a = WInd Int a -- -- data Foo { subVal :: Bar } -- -- instance $(sayableSubConstraints $ do ofType ''Foo subWrapper (TH.ConT -- ''WithIndentation) ) => Sayable saytag Foo where sayable foo = -- FOO &< WInd 2 (subVal foo) subWrapper :: Type -> ConstrM () -- | This ConstrM operation is used to declare a filter to be applied to -- the sub-elements: only sub-element Name candidates for which -- this filter function returns True will have a Sayable -- constraint generated. -- --
-- myModuleNames :: TH.Name -> Bool -- myModuleNames = maybe False (MyModule ==) . TH.nameModule -- -- instance $(sayableSubConstraints $ do ofType ''Foo -- subElemFilter myModuleNames -- ) => Sayable saytag Foo where ... ---- -- If not used, the default is equivalent to subElemFilter (const -- True) which accepts all sub-element names. subElemFilter :: (Name -> Bool) -> ConstrM () -- | This ConstrM operation is used to specify the variable name that -- should be used for the next sub-element parameter. When sub-elements -- are parameterized (e.g. Maybe a) then this operation allows -- the identification of a primary instance variable to be used for the -- sub-element. All sub-elements must take parameters in the same order, -- although they don't need to accept all parameters. -- --
-- data Foo b a = Foo (Either a b) -- -- instance $(sayableSubConstraints $ do ofType ''Foo -- paramVar "x" -- paramVar "y" -- ) => Sayable saytag (Foo y x) where ... ---- -- generates: (Sayable saytag (Either x y), Sayable saytag x, Sayable -- saytag y) -- -- As shown above, this operation can be used multiple times to specify -- multiple parameters. paramVar :: String -> ConstrM () -- | This ConstrM operation is used to specify a Symbol singleton value -- that should be used for the next sub-element parameter. When -- sub-elements are parameterized (e.g. Maybe a) then this -- operation allows the identification of a Symbol to be used for the -- sub-element. All sub-elements must take parameters in the same order, -- although they don't need to accept all parameters. -- --
-- data Bar (s :: Symbol) = ...
-- data Foo (s :: Symbol) = Foo { thing :: Bar s }
--
-- instance $(sayableSubConstraints $ do ofType ''Foo
-- paramSym "arg"
-- ) => Sayable saytag (Foo "arg") where ...
--
--
-- generates: Sayable saytag (Bar "arg")
--
-- This operation can be used multiple times to specify multiple
-- parameters.
paramSym :: String -> ConstrM ()
-- | This ConstrM operation is used to specify a Nat singleton value that
-- should be used for the next sub-element parameter. When sub-elements
-- are parameterized (e.g. Maybe a) then this operation allows
-- the identification of a Nat to be used for the sub-element. All
-- sub-elements must take parameters in the same order, although they
-- don't need to accept all parameters.
--
--
-- data Bar (s :: Symbol) (n :: Nat) = ...
-- data Foo (n :: Nat) (s :: Symbol) = Foo { thing :: Bar s n }
--
-- instance $(sayableSubConstraints $ do ofType ''Foo
-- paramSym "arg"
-- paramNat 2
-- ) => Sayable saytag (Foo 2 "arg") where ...
--
--
-- generates: Sayable saytag (Bar "arg" 2)
--
-- This operation can be used multiple times to specify multiple
-- parameters.
paramNat :: Integer -> ConstrM ()
-- | This ConstrM operation is used to specify a template-haskell Type that
-- should be used for the next sub-element parameter. When sub-elements
-- are parameterized (e.g. Maybe a) then this operation allows
-- specification of a specific Type for that parameter. The use of this
-- operation is unusual and is expected only in cases where
-- paramVar, paramSym, or paramNat are insufficient.
--
--
-- data Bar '(s :: Symbol, n :: Nat) = ...
-- data Foo (n :: Nat) (s :: Symbol) = Foo { thing :: Bar '(s, n) }
--
-- instance $(sayableSubConstraints $ do ofType ''Foo
-- paramTH (TH.App
-- (TH.App (TH.TupleT 2)
-- (TH.LitT $ TH.StrTyLit "loud"))
-- (TH.LitT $ TH.NumTyLit 3))
-- ) => Sayable saytag (Foo 3 "loud") where ...
--
--
-- generates: Sayable saytag (Bar '("loud", 3))
--
-- This operation can be used multiple times to specify multiple
-- parameters.
paramTH :: Type -> ConstrM ()
instance GHC.Base.Applicative Text.Sayable.ConstrM
instance GHC.Base.Functor Text.Sayable.ConstrM
instance GHC.Base.Monad Text.Sayable.ConstrM
instance (tagA GHC.Types.~ tagB) => Text.Sayable.Sayable tagA (Text.Sayable.Saying tagB)
instance Text.Sayable.Sayable tag Data.Text.Internal.Text
instance Text.Sayable.Sayable tag GHC.Base.String
instance Text.Sayable.Sayable tag GHC.Types.Char
instance Text.Sayable.Sayable tag GHC.Types.Bool
instance Text.Sayable.Sayable tag GHC.Types.Int
instance Text.Sayable.Sayable tag GHC.Num.Integer.Integer
instance Text.Sayable.Sayable tag GHC.Int.Int32
instance Text.Sayable.Sayable tag GHC.Int.Int64
instance Text.Sayable.Sayable tag GHC.Word.Word8
instance Text.Sayable.Sayable tag GHC.Word.Word16
instance Text.Sayable.Sayable tag GHC.Word.Word32
instance Text.Sayable.Sayable tag GHC.Word.Word64
instance Text.Sayable.Sayable tag GHC.Num.Natural.Natural
instance Text.Sayable.Sayable tag GHC.Types.Float
instance Text.Sayable.Sayable tag GHC.Types.Double
instance Text.Sayable.Sayable tag Data.Text.Internal.Lazy.Text
instance Text.Sayable.Sayable tag Data.ByteString.Internal.Type.ByteString
instance Text.Sayable.Sayable tag Data.ByteString.Lazy.Internal.ByteString
instance Text.Sayable.Sayable tag GHC.Exception.Type.SomeException
instance Text.Sayable.Sayable tag (Prettyprinter.Internal.Doc Text.Sayable.SayableAnn)
instance Text.Sayable.Sayable tag (Prettyprinter.Internal.Doc ann)
instance GHC.Base.Semigroup (Text.Sayable.Saying tag)
instance Prettyprinter.Internal.Pretty (Text.Sayable.Saying tag)
instance GHC.TypeLits.KnownSymbol ann => GHC.OverloadedLabels.IsLabel ann Text.Sayable.SayableAnn