packages feed

text-display 0.0.4.0 → 0.0.5.0

raw patch · 6 files changed

+607/−378 lines, 6 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Data.Text.Display: instance (Data.Text.Display.Display a, Data.Text.Display.Display b) => Data.Text.Display.Display (a, b)
- Data.Text.Display: instance (Data.Text.Display.Display a, Data.Text.Display.Display b, Data.Text.Display.Display c) => Data.Text.Display.Display (a, b, c)
- Data.Text.Display: instance (Data.Text.Display.Display a, Data.Text.Display.Display b, Data.Text.Display.Display c, Data.Text.Display.Display d) => Data.Text.Display.Display (a, b, c, d)
- Data.Text.Display: instance Data.Text.Display.CannotDisplayBareFunctions => Data.Text.Display.Display (a -> b)
- Data.Text.Display: instance Data.Text.Display.CannotDisplayByteStrings => Data.Text.Display.Display Data.ByteString.Internal.ByteString
- Data.Text.Display: instance Data.Text.Display.CannotDisplayByteStrings => Data.Text.Display.Display Data.ByteString.Lazy.Internal.ByteString
- Data.Text.Display: instance Data.Text.Display.Display ()
- Data.Text.Display: instance Data.Text.Display.Display Data.Text.Internal.Lazy.Text
- Data.Text.Display: instance Data.Text.Display.Display Data.Text.Internal.Text
- Data.Text.Display: instance Data.Text.Display.Display Data.Void.Void
- Data.Text.Display: instance Data.Text.Display.Display GHC.Exception.Type.SomeException
- Data.Text.Display: instance Data.Text.Display.Display GHC.IO.Exception.IOException
- Data.Text.Display: instance Data.Text.Display.Display GHC.Int.Int16
- Data.Text.Display: instance Data.Text.Display.Display GHC.Int.Int32
- Data.Text.Display: instance Data.Text.Display.Display GHC.Int.Int64
- Data.Text.Display: instance Data.Text.Display.Display GHC.Int.Int8
- Data.Text.Display: instance Data.Text.Display.Display GHC.Num.Integer.Integer
- Data.Text.Display: instance Data.Text.Display.Display GHC.Types.Bool
- Data.Text.Display: instance Data.Text.Display.Display GHC.Types.Char
- Data.Text.Display: instance Data.Text.Display.Display GHC.Types.Double
- Data.Text.Display: instance Data.Text.Display.Display GHC.Types.Float
- Data.Text.Display: instance Data.Text.Display.Display GHC.Types.Int
- Data.Text.Display: instance Data.Text.Display.Display GHC.Types.Word
- Data.Text.Display: instance Data.Text.Display.Display GHC.Word.Word16
- Data.Text.Display: instance Data.Text.Display.Display GHC.Word.Word32
- Data.Text.Display: instance Data.Text.Display.Display GHC.Word.Word64
- Data.Text.Display: instance Data.Text.Display.Display GHC.Word.Word8
- Data.Text.Display: instance Data.Text.Display.Display a => Data.Text.Display.Display (GHC.Base.NonEmpty a)
- Data.Text.Display: instance Data.Text.Display.Display a => Data.Text.Display.Display (GHC.Maybe.Maybe a)
- Data.Text.Display: instance Data.Text.Display.Display a => Data.Text.Display.Display [a]
- Data.Text.Display: instance GHC.Classes.Eq e => GHC.Classes.Eq (Data.Text.Display.DisplayDecimal e)
- Data.Text.Display: instance GHC.Classes.Eq e => GHC.Classes.Eq (Data.Text.Display.DisplayRealFloat e)
- Data.Text.Display: instance GHC.Classes.Ord e => GHC.Classes.Ord (Data.Text.Display.DisplayDecimal e)
- Data.Text.Display: instance GHC.Classes.Ord e => GHC.Classes.Ord (Data.Text.Display.DisplayRealFloat e)
- Data.Text.Display: instance GHC.Enum.Enum e => GHC.Enum.Enum (Data.Text.Display.DisplayDecimal e)
- Data.Text.Display: instance GHC.Float.Floating e => GHC.Float.Floating (Data.Text.Display.DisplayRealFloat e)
- Data.Text.Display: instance GHC.Float.RealFloat e => Data.Text.Display.Display (Data.Text.Display.DisplayRealFloat e)
- Data.Text.Display: instance GHC.Float.RealFloat e => GHC.Float.RealFloat (Data.Text.Display.DisplayRealFloat e)
- Data.Text.Display: instance GHC.Num.Num e => GHC.Num.Num (Data.Text.Display.DisplayDecimal e)
- Data.Text.Display: instance GHC.Num.Num e => GHC.Num.Num (Data.Text.Display.DisplayRealFloat e)
- Data.Text.Display: instance GHC.Real.Fractional e => GHC.Real.Fractional (Data.Text.Display.DisplayRealFloat e)
- Data.Text.Display: instance GHC.Real.Integral e => Data.Text.Display.Display (Data.Text.Display.DisplayDecimal e)
- Data.Text.Display: instance GHC.Real.Integral e => GHC.Real.Integral (Data.Text.Display.DisplayDecimal e)
- Data.Text.Display: instance GHC.Real.Real e => GHC.Real.Real (Data.Text.Display.DisplayDecimal e)
- Data.Text.Display: instance GHC.Real.Real e => GHC.Real.Real (Data.Text.Display.DisplayRealFloat e)
- Data.Text.Display: instance GHC.Real.RealFrac e => GHC.Real.RealFrac (Data.Text.Display.DisplayRealFloat e)
- Data.Text.Display: instance GHC.Show.Show a => GHC.Show.Show (Data.Text.Display.ShowInstance a)
- Data.Text.Display: instance GHC.Show.Show e => Data.Text.Display.Display (Data.Text.Display.ShowInstance e)
- Data.Text.Display: instance GHC.TypeLits.KnownSymbol str => Data.Text.Display.Display (Data.Text.Display.OpaqueInstance str a)
+ Data.Text.Display: RecordInstance :: a -> RecordInstance a
+ Data.Text.Display: [unDisplayProduct] :: RecordInstance a -> a
+ Data.Text.Display: newtype RecordInstance a
+ Data.Text.Display.Core: DisplayDecimal :: e -> DisplayDecimal e
+ Data.Text.Display.Core: DisplayRealFloat :: e -> DisplayRealFloat e
+ Data.Text.Display.Core: Opaque :: a -> OpaqueInstance (str :: Symbol) (a :: Type)
+ Data.Text.Display.Core: ShowInstance :: a -> ShowInstance (a :: Type)
+ Data.Text.Display.Core: class Display a
+ Data.Text.Display.Core: display :: Display a => a -> Text
+ Data.Text.Display.Core: displayBuilder :: Display a => a -> Builder
+ Data.Text.Display.Core: displayList :: Display a => [a] -> Builder
+ Data.Text.Display.Core: displayParen :: Bool -> Builder -> Builder
+ Data.Text.Display.Core: displayPrec :: Display a => Int -> a -> Builder
+ Data.Text.Display.Core: instance (Data.Text.Display.Core.Display a, Data.Text.Display.Core.Display b) => Data.Text.Display.Core.Display (a, b)
+ Data.Text.Display.Core: instance (Data.Text.Display.Core.Display a, Data.Text.Display.Core.Display b, Data.Text.Display.Core.Display c) => Data.Text.Display.Core.Display (a, b, c)
+ Data.Text.Display.Core: instance (Data.Text.Display.Core.Display a, Data.Text.Display.Core.Display b, Data.Text.Display.Core.Display c, Data.Text.Display.Core.Display d) => Data.Text.Display.Core.Display (a, b, c, d)
+ Data.Text.Display.Core: instance Data.Text.Display.Core.CannotDisplayBareFunctions => Data.Text.Display.Core.Display (a -> b)
+ Data.Text.Display.Core: instance Data.Text.Display.Core.CannotDisplayByteStrings => Data.Text.Display.Core.Display Data.ByteString.Internal.ByteString
+ Data.Text.Display.Core: instance Data.Text.Display.Core.CannotDisplayByteStrings => Data.Text.Display.Core.Display Data.ByteString.Lazy.Internal.ByteString
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display ()
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display Data.Text.Internal.Lazy.Text
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display Data.Text.Internal.Text
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display Data.Void.Void
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Exception.Type.SomeException
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.IO.Exception.IOException
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Int.Int16
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Int.Int32
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Int.Int64
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Int.Int8
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Num.Integer.Integer
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Types.Bool
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Types.Char
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Types.Double
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Types.Float
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Types.Int
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Types.Word
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Word.Word16
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Word.Word32
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Word.Word64
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display GHC.Word.Word8
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display a => Data.Text.Display.Core.Display (GHC.Base.NonEmpty a)
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display a => Data.Text.Display.Core.Display (GHC.Maybe.Maybe a)
+ Data.Text.Display.Core: instance Data.Text.Display.Core.Display a => Data.Text.Display.Core.Display [a]
+ Data.Text.Display.Core: instance GHC.Classes.Eq e => GHC.Classes.Eq (Data.Text.Display.Core.DisplayDecimal e)
+ Data.Text.Display.Core: instance GHC.Classes.Eq e => GHC.Classes.Eq (Data.Text.Display.Core.DisplayRealFloat e)
+ Data.Text.Display.Core: instance GHC.Classes.Ord e => GHC.Classes.Ord (Data.Text.Display.Core.DisplayDecimal e)
+ Data.Text.Display.Core: instance GHC.Classes.Ord e => GHC.Classes.Ord (Data.Text.Display.Core.DisplayRealFloat e)
+ Data.Text.Display.Core: instance GHC.Enum.Enum e => GHC.Enum.Enum (Data.Text.Display.Core.DisplayDecimal e)
+ Data.Text.Display.Core: instance GHC.Float.Floating e => GHC.Float.Floating (Data.Text.Display.Core.DisplayRealFloat e)
+ Data.Text.Display.Core: instance GHC.Float.RealFloat e => Data.Text.Display.Core.Display (Data.Text.Display.Core.DisplayRealFloat e)
+ Data.Text.Display.Core: instance GHC.Float.RealFloat e => GHC.Float.RealFloat (Data.Text.Display.Core.DisplayRealFloat e)
+ Data.Text.Display.Core: instance GHC.Num.Num e => GHC.Num.Num (Data.Text.Display.Core.DisplayDecimal e)
+ Data.Text.Display.Core: instance GHC.Num.Num e => GHC.Num.Num (Data.Text.Display.Core.DisplayRealFloat e)
+ Data.Text.Display.Core: instance GHC.Real.Fractional e => GHC.Real.Fractional (Data.Text.Display.Core.DisplayRealFloat e)
+ Data.Text.Display.Core: instance GHC.Real.Integral e => Data.Text.Display.Core.Display (Data.Text.Display.Core.DisplayDecimal e)
+ Data.Text.Display.Core: instance GHC.Real.Integral e => GHC.Real.Integral (Data.Text.Display.Core.DisplayDecimal e)
+ Data.Text.Display.Core: instance GHC.Real.Real e => GHC.Real.Real (Data.Text.Display.Core.DisplayDecimal e)
+ Data.Text.Display.Core: instance GHC.Real.Real e => GHC.Real.Real (Data.Text.Display.Core.DisplayRealFloat e)
+ Data.Text.Display.Core: instance GHC.Real.RealFrac e => GHC.Real.RealFrac (Data.Text.Display.Core.DisplayRealFloat e)
+ Data.Text.Display.Core: instance GHC.Show.Show a => GHC.Show.Show (Data.Text.Display.Core.ShowInstance a)
+ Data.Text.Display.Core: instance GHC.Show.Show e => Data.Text.Display.Core.Display (Data.Text.Display.Core.ShowInstance e)
+ Data.Text.Display.Core: instance GHC.TypeLits.KnownSymbol str => Data.Text.Display.Core.Display (Data.Text.Display.Core.OpaqueInstance str a)
+ Data.Text.Display.Core: newtype DisplayDecimal e
+ Data.Text.Display.Core: newtype DisplayRealFloat e
+ Data.Text.Display.Core: newtype OpaqueInstance (str :: Symbol) (a :: Type)
+ Data.Text.Display.Core: newtype ShowInstance (a :: Type)
+ Data.Text.Display.Core: type family CannotDisplayByteStrings :: Constraint
+ Data.Text.Display.Generic: RecordInstance :: a -> RecordInstance a
+ Data.Text.Display.Generic: [unDisplayProduct] :: RecordInstance a -> a
+ Data.Text.Display.Generic: class Assert (pred :: Bool) (msg :: ErrorMessage)
+ Data.Text.Display.Generic: class GDisplay1 f
+ Data.Text.Display.Generic: gdisplayBuilder1 :: GDisplay1 f => f p -> Builder
+ Data.Text.Display.Generic: gdisplayBuilderDefault :: (Generic a, GDisplay1 (Rep a)) => a -> Builder
+ Data.Text.Display.Generic: instance ((TypeError ...) GHC.Types.~ '()) => Data.Text.Display.Generic.Assert 'GHC.Types.False msg
+ Data.Text.Display.Generic: instance (Data.Text.Display.Generic.AssertNoSumRecordInstance Data.Text.Display.Core.Display a, GHC.Generics.Generic a, Data.Text.Display.Generic.GDisplay1 (GHC.Generics.Rep a)) => Data.Text.Display.Core.Display (Data.Text.Display.Generic.RecordInstance a)
+ Data.Text.Display.Generic: instance (Data.Text.Display.Generic.GDisplay1 a, Data.Text.Display.Generic.GDisplay1 b) => Data.Text.Display.Generic.GDisplay1 (a GHC.Generics.:*: b)
+ Data.Text.Display.Generic: instance (Data.Text.Display.Generic.GDisplay1 a, Data.Text.Display.Generic.GDisplay1 b) => Data.Text.Display.Generic.GDisplay1 (a GHC.Generics.:+: b)
+ Data.Text.Display.Generic: instance (GHC.Generics.Constructor c, Data.Text.Display.Generic.GDisplay1 f) => Data.Text.Display.Generic.GDisplay1 (GHC.Generics.M1 GHC.Generics.C c f)
+ Data.Text.Display.Generic: instance (GHC.Generics.Selector s, Data.Text.Display.Generic.GDisplay1 f) => Data.Text.Display.Generic.GDisplay1 (GHC.Generics.M1 GHC.Generics.S s f)
+ Data.Text.Display.Generic: instance Data.Text.Display.Core.Display c => Data.Text.Display.Generic.GDisplay1 (GHC.Generics.K1 i c)
+ Data.Text.Display.Generic: instance Data.Text.Display.Generic.Assert 'GHC.Types.True msg
+ Data.Text.Display.Generic: instance Data.Text.Display.Generic.GDisplay1 GHC.Generics.U1
+ Data.Text.Display.Generic: instance Data.Text.Display.Generic.GDisplay1 GHC.Generics.V1
+ Data.Text.Display.Generic: instance Data.Text.Display.Generic.GDisplay1 f => Data.Text.Display.Generic.GDisplay1 (GHC.Generics.M1 GHC.Generics.D s f)
+ Data.Text.Display.Generic: instance GHC.Generics.Generic a => GHC.Generics.Generic (Data.Text.Display.Generic.RecordInstance a)
+ Data.Text.Display.Generic: newtype RecordInstance a
+ Data.Text.Display.Generic: type AssertNoSumRecordInstance (constraint :: Type -> Constraint) a = Assert (Not (HasSum (Rep a))) ('Text "🚫 Cannot derive " ':<>: 'ShowType constraint ':<>: 'Text " instance for " ':<>: 'ShowType a ':<>: 'Text " via RecordInstance due to sum type" ':$$: 'Text "💡 Sum types should use a manual instance or derive one via ShowInstance.")
+ Data.Text.Display.Generic: type family HasSum f

Files

CHANGELOG.md view
@@ -1,6 +1,10 @@ # CHANGELOG -## [Unreleased]+## [v0.0.5.0] - 16/05/2023++* Add Generic machinery (GDisplay1) and RecordInstance newtype for deriving via (https://github.com/haskell-text/text-display/pull/52)++## [v0.0.4.0] - 21/03/2023  * Fix `displayList` by making it lazier (https://github.com/haskell-text/text-display/pull/27) * Add Display instance for `Void` (https://github.com/haskell-text/text-display/pull/28)
README.md view
@@ -24,7 +24,7 @@ > *A Typeclass for user-facing output*  -The `text-display` library offers the `Display` for developers to print a textual representation of datatypes that does not+The `text-display` library offers the `Display` typeclass for developers to print a textual representation of datatypes that does not have to abide by the rules of the [Show typeclass][Show].  ## Documentation@@ -35,7 +35,7 @@ * [Comparison with other libraries][comparison]  [Show]: https://hackage.haskell.org/package/base/docs/Text-Show.html#v:Show-[tutorial]: https://hackage.haskell.org/package/text-display/src/docs/book/Tutorial.html-[design]: https://hackage.haskell.org/package/text-display/src/docs/book/Design.html+[tutorial]: https://hackage.haskell.org/package/text-display/docs/doc/book/Tutorial.html+[design]: https://hackage.haskell.org/package/text-display/docs/doc/book/Design.html [reference]: https://hackage.haskell.org/package/text-display/docs/Data-Text-Display.html-[comparison]: https://hackage.haskell.org/package/text-display/src/docs/book/Comparison.html+[comparison]: https://hackage.haskell.org/package/text-display/docs/doc/book/Comparison.html
src/Data/Text/Display.hs view
@@ -1,15 +1,3 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DerivingVia #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE StrictData #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE UndecidableInstances #-}- -- | --  Module      : Data.Text.Display --  Copyright   : © Hécate Moonlight, 2021@@ -26,6 +14,7 @@     -- * Deriving your instance automatically   , ShowInstance (..)   , OpaqueInstance (..)+  , RecordInstance (..)      -- * Writing your instance by hand   , displayParen@@ -35,363 +24,8 @@   ) where -import Control.Exception hiding (TypeError)-import Data.ByteString-import qualified Data.ByteString.Lazy as BL-import Data.Int-import Data.Kind-import Data.List.NonEmpty-import Data.Proxy-import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Text.Lazy as TL-import Data.Text.Lazy.Builder (Builder)-import qualified Data.Text.Lazy.Builder as TB-import qualified Data.Text.Lazy.Builder.Int as TB-import qualified Data.Text.Lazy.Builder.RealFloat as TB-import Data.Void (Void)-import Data.Word-import GHC.TypeLits---- | A typeclass for user-facing output.------ @since 0.0.1.0-class Display a where-  {-# MINIMAL displayBuilder | displayPrec #-}--  -- | Implement this method to describe how to convert your value to 'Builder'.-  displayBuilder :: a -> Builder-  displayBuilder = displayPrec 0--  -- | The method 'displayList' is provided to allow for a specialised-  -- way to render lists of a certain value.-  -- This is used to render the list of 'Char' as a string of characters-  -- enclosed in double quotes, rather than between square brackets and-  -- separated by commas.-  ---  -- === Example-  ---  -- > import qualified Data.Text.Lazy.Builder as TB-  -- >-  -- > instance Display Char where-  -- >   displayBuilder c = TB.fromText $ T.singleton c-  -- >   displayList cs = TB.fromText $ T.pack cs-  -- >-  -- > instance Display a => Display [a] where-  -- >   -- In this instance, 'displayBuilder' is defined in terms of 'displayList', which for most types-  -- >   -- is defined as the default written in the class declaration.-  -- >   -- But when a ~ Char, there is an explicit implementation that is selected instead, which-  -- >   -- provides the rendering of the character string between double quotes.-  -- >   displayBuilder = displayList-  ---  -- ==== How implementations are selected-  ---  -- > displayBuilder ([1,2,3] :: [Int])-  -- > → displayBuilder @[Int] = displayBuilderList @Int-  -- > → Default `displayList`-  -- >-  -- > displayBuilder ("abc" :: [Char])-  -- > → displayBuilder @[Char] = displayBuilderList @Char-  -- > → Custom `displayList`-  displayList :: [a] -> Builder-  displayList [] = "[]"-  displayList (x : xs) = "[" <> displayBuilder x <> foldMap go xs <> "]"-    where-      go :: a -> Builder-      go y = "," <> displayBuilder y--  -- | The method 'displayPrec' allows you to write instances that-  -- require nesting. The precedence parameter can be thought of as a-  -- suggestion coming from the surrounding context for how tightly to bind. If the precedence-  -- parameter is higher than the precedence of the operator (or constructor, function, etc.)-  -- being displayed, then that suggests that the output will need to be surrounded in parentheses-  -- in order to bind tightly enough (see 'displayParen').-  ---  -- For example, if an operator constructor is being displayed, then the precedence requirement-  -- for its arguments will be the precedence of the operator. Meaning, if the argument-  -- binds looser than the surrounding operator, then it will require parentheses.-  ---  -- Note that function/constructor application has an effective precedence of 10.-  ---  -- === Examples-  ---  -- > instance Display a => Display (Maybe a) where-  -- >   -- In this instance, we define 'displayPrec' rather than 'displayBuilder' as we need to decide-  -- >   -- whether or not to surround ourselves in parentheses based on the surrounding context.-  -- >   -- If the precedence parameter is higher than 10 (the precedence of constructor application)-  -- >   -- then we indeed need to surround ourselves in parentheses to avoid malformed outputs-  -- >   -- such as @Just Just 5@.-  -- >   -- We then set the precedence parameter of the inner 'displayPrec' to 11, as even-  -- >   -- constructor application is not strong enough to avoid parentheses.-  -- >   displayPrec _ Nothing = "Nothing"-  -- >   displayPrec prec (Just a) = displayParen (prec > 10) $ "Just " <> displayPrec 11 a-  ---  -- > data Pair a b = a :*: b-  -- > infix 5 :*: -- arbitrary choice of precedence-  -- > instance (Display a, Display b) => Display (Pair a b) where-  -- >   displayPrec prec (a :*: b) = displayParen (prec > 5) $ displayPrec 6 a <> " :*: " <> displayPrec 6 b-  displayPrec-    :: Int-    -- ^ The precedence level passed in by the surrounding context-    -> a-    -> Builder-  displayPrec _ = displayBuilder---- | Convert a value to a readable 'Text'.------ === Examples--- >>> display 3--- "3"------ >>> display True--- "True"------ @since 0.0.1.0-display :: Display a => a -> Text-display a = TL.toStrict $ TB.toLazyText $ displayBuilder a---- | 🚫 You should not try to display functions!------ 💡 Write a 'newtype' wrapper that represents your domain more accurately.---    If you are not consciously trying to use 'display' on a function,---    make sure that you are not missing an argument somewhere.------ @since 0.0.1.0-instance CannotDisplayBareFunctions => Display (a -> b) where-  displayBuilder = undefined---- | @since 0.0.1.0-type family CannotDisplayBareFunctions :: Constraint where-  CannotDisplayBareFunctions =-    TypeError-      ( 'Text "🚫 You should not try to display functions!"-          ':$$: 'Text "💡 Write a 'newtype' wrapper that represents your domain more accurately."-          ':$$: 'Text "   If you are not consciously trying to use `display` on a function,"-          ':$$: 'Text "   make sure that you are not missing an argument somewhere."-      )---- | 🚫 You should not try to display strict ByteStrings!------ 💡 Always provide an explicit encoding.--- Use 'Data.Text.Encoding.decodeUtf8'' or 'Data.Text.Encoding.decodeUtf8With' to convert from UTF-8------ @since 0.0.1.0-instance CannotDisplayByteStrings => Display ByteString where-  displayBuilder = undefined---- | 🚫 You should not try to display lazy ByteStrings!------ 💡 Always provide an explicit encoding.--- Use 'Data.Text.Encoding.decodeUtf8'' or 'Data.Text.Encoding.decodeUtf8With' to convert from UTF-8------ @since 0.0.1.0-instance CannotDisplayByteStrings => Display BL.ByteString where-  displayBuilder = undefined--type family CannotDisplayByteStrings :: Constraint where-  CannotDisplayByteStrings =-    TypeError-      ( 'Text "🚫 You should not try to display ByteStrings!"-          ':$$: 'Text "💡 Always provide an explicit encoding"-          ':$$: 'Text "Use 'Data.Text.Encoding.decodeUtf8'' or 'Data.Text.Encoding.decodeUtf8With' to convert from UTF-8"-      )---- | A utility function that surrounds the given 'Builder' with parentheses when the Bool parameter is True.--- Useful for writing instances that may require nesting. See the 'displayPrec' documentation for more--- information.------ @since 0.0.1.0-displayParen :: Bool -> Builder -> Builder-displayParen b txt = if b then "(" <> txt <> ")" else txt---- | This wrapper allows you to create an opaque instance for your type,--- useful for redacting sensitive content like tokens or passwords.------ === Example------ > data UserToken = UserToken UUID--- >  deriving Display--- >    via (OpaqueInstance "[REDACTED]" UserToken)------ > display $ UserToken "7a01d2ce-31ff-11ec-8c10-5405db82c3cd"--- > "[REDACTED]"------ @since 0.0.1.0-newtype OpaqueInstance (str :: Symbol) (a :: Type) = Opaque a---- | This wrapper allows you to create an opaque instance for your type,--- useful for redacting sensitive content like tokens or passwords.------ @since 0.0.1.0-instance KnownSymbol str => Display (OpaqueInstance str a) where-  displayBuilder _ = TB.fromString $ symbolVal (Proxy @str)---- | This wrapper allows you to rely on a pre-existing 'Show' instance in order to--- derive 'Display' from it.------ === Example------ > data AutomaticallyDerived = AD--- >  -- We derive 'Show'--- >  deriving stock Show--- >  -- We take advantage of the 'Show' instance to derive 'Display' from it--- >  deriving Display--- >    via (ShowInstance AutomaticallyDerived)------ @since 0.0.1.0-newtype ShowInstance (a :: Type)-  = ShowInstance a-  deriving newtype-    ( Show-      -- ^ @since 0.0.1.0-    )---- | This wrapper allows you to rely on a pre-existing 'Show' instance in order to derive 'Display' from it.------ @since 0.0.1.0-instance Show e => Display (ShowInstance e) where-  displayBuilder s = TB.fromString $ show s---- @since 0.0.1.0-newtype DisplayDecimal e-  = DisplayDecimal e-  deriving newtype-    (Integral, Real, Enum, Ord, Num, Eq)---- @since 0.0.1.0-instance Integral e => Display (DisplayDecimal e) where-  displayBuilder = TB.decimal---- @since 0.0.1.0-newtype DisplayRealFloat e-  = DisplayRealFloat e-  deriving newtype-    (RealFloat, RealFrac, Real, Ord, Eq, Num, Fractional, Floating)---- @since 0.0.1.0-instance RealFloat e => Display (DisplayRealFloat e) where-  displayBuilder = TB.realFloat---- | @since 0.0.1.0-deriving via (ShowInstance ()) instance Display ()---- | @since 0.0.3.0-deriving via (ShowInstance Void) instance Display Void---- | @since 0.0.1.0-deriving via (ShowInstance Bool) instance Display Bool---- | @since 0.0.1.0--- 'displayList' is overloaded, so that when the @Display [a]@ instance calls 'displayList',--- we end up with a nice string instead of a list of chars between brackets.------ >>> display [1, 2, 3]--- "[1,2,3]"------ >>> display ['h', 'e', 'l', 'l', 'o']--- "hello"-instance Display Char where-  -- This instance's implementation is used in the haddocks of the typeclass.-  -- If you change it, reflect the change in the documentation.-  displayBuilder c = TB.fromText $ T.singleton c-  displayList cs = TB.fromText $ T.pack cs---- | Lazy 'TL.Text'------ @since 0.0.1.0-instance Display TL.Text where-  displayBuilder = TB.fromLazyText---- | Strict 'Data.Text.Text'------ @since 0.0.1.0-instance Display Text where-  displayBuilder = TB.fromText---- | @since 0.0.1.0-instance Display a => Display [a] where-  {-# SPECIALIZE instance Display [String] #-}-  {-# SPECIALIZE instance Display [Char] #-}-  {-# SPECIALIZE instance Display [Int] #-}--  -- In this instance, 'displayBuilder' is defined in terms of 'displayList', which for most types-  -- is defined as the default written in the class declaration.-  -- But when @a ~ Char@, there is an explicit implementation that is selected instead, which-  -- provides the rendering of the character string between double quotes.-  displayBuilder = displayList---- | @since 0.0.1.0-instance Display a => Display (NonEmpty a) where-  displayBuilder (a :| as) = displayBuilder a <> TB.fromString " :| " <> displayBuilder as---- | @since 0.0.1.0-instance Display a => Display (Maybe a) where-  -- In this instance, we define 'displayPrec' rather than 'displayBuilder' as we need to decide-  -- whether or not to surround ourselves in parentheses based on the surrounding context.-  -- If the precedence parameter is higher than 10 (the precedence of constructor application)-  -- then we indeed need to surround ourselves in parentheses to avoid malformed outputs-  -- such as @Just Just 5@.-  -- We then set the precedence parameter of the inner 'displayPrec' to 11, as even-  -- constructor application is not strong enough to avoid parentheses.-  displayPrec _ Nothing = "Nothing"-  displayPrec prec (Just a) = displayParen (prec > 10) $ "Just " <> displayPrec 11 a---- | @since 0.0.1.0-deriving via (DisplayRealFloat Double) instance Display Double---- | @since 0.0.1.0-deriving via (DisplayRealFloat Float) instance Display Float---- | @since 0.0.1.0-deriving via (DisplayDecimal Int) instance Display Int---- | @since 0.0.1.0-deriving via (DisplayDecimal Int8) instance Display Int8---- | @since 0.0.1.0-deriving via (DisplayDecimal Int16) instance Display Int16---- | @since 0.0.1.0-deriving via (DisplayDecimal Int32) instance Display Int32---- | @since 0.0.1.0-deriving via (DisplayDecimal Int64) instance Display Int64---- | @since 0.0.1.0-deriving via (DisplayDecimal Integer) instance Display Integer---- | @since 0.0.1.0-deriving via (DisplayDecimal Word) instance Display Word---- | @since 0.0.1.0-deriving via (DisplayDecimal Word8) instance Display Word8---- | @since 0.0.1.0-deriving via (DisplayDecimal Word16) instance Display Word16---- | @since 0.0.1.0-deriving via (DisplayDecimal Word32) instance Display Word32---- | @since 0.0.1.0-deriving via (DisplayDecimal Word64) instance Display Word64---- | @since 0.0.1.0-deriving via (ShowInstance IOException) instance Display IOException---- | @since 0.0.1.0-deriving via (ShowInstance SomeException) instance Display SomeException---- | @since 0.0.1.0-instance (Display a, Display b) => Display (a, b) where-  displayBuilder (a, b) = "(" <> displayBuilder a <> "," <> displayBuilder b <> ")"---- | @since 0.0.1.0-instance (Display a, Display b, Display c) => Display (a, b, c) where-  displayBuilder (a, b, c) = "(" <> displayBuilder a <> "," <> displayBuilder b <> "," <> displayBuilder c <> ")"---- | @since 0.0.1.0-instance (Display a, Display b, Display c, Display d) => Display (a, b, c, d) where-  displayBuilder (a, b, c, d) = "(" <> displayBuilder a <> "," <> displayBuilder b <> "," <> displayBuilder c <> "," <> displayBuilder d <> ")"+import Data.Text.Display.Core+import Data.Text.Display.Generic  -- $designChoices --
+ src/Data/Text/Display/Core.hs view
@@ -0,0 +1,417 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE StrictData #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++-- |+--  Module      : Data.Text.Display.Core+--  Copyright   : © Hécate Moonlight, 2021+--  License     : MIT+--  Maintainer  : hecate@glitchbra.in+--  Stability   : stable+--+--  Core 'Display' typeclass and instances+module Data.Text.Display.Core where++import Control.Exception hiding (TypeError)+import Data.ByteString+import qualified Data.ByteString.Lazy as BL+import Data.Int+import Data.Kind+import Data.List.NonEmpty+import Data.Proxy+import Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import Data.Text.Lazy.Builder (Builder)+import qualified Data.Text.Lazy.Builder as TB+import qualified Data.Text.Lazy.Builder.Int as TB+import qualified Data.Text.Lazy.Builder.RealFloat as TB+import Data.Void (Void)+import Data.Word+import GHC.TypeLits++-- | A typeclass for user-facing output.+--+-- @since 0.0.1.0+class Display a where+  {-# MINIMAL displayBuilder | displayPrec #-}++  -- | Implement this method to describe how to convert your value to 'Builder'.+  displayBuilder :: a -> Builder+  displayBuilder = displayPrec 0++  -- | The method 'displayList' is provided to allow for a specialised+  -- way to render lists of a certain value.+  -- This is used to render the list of 'Char' as a string of characters+  -- enclosed in double quotes, rather than between square brackets and+  -- separated by commas.+  --+  -- === Example+  --+  -- > import qualified Data.Text.Lazy.Builder as TB+  -- >+  -- > instance Display Char where+  -- >   displayBuilder c = TB.fromText $ T.singleton c+  -- >   displayList cs = TB.fromText $ T.pack cs+  -- >+  -- > instance (Display a) => Display [a] where+  -- >   -- In this instance, 'displayBuilder' is defined in terms of 'displayList', which for most types+  -- >   -- is defined as the default written in the class declaration.+  -- >   -- But when a ~ Char, there is an explicit implementation that is selected instead, which+  -- >   -- provides the rendering of the character string between double quotes.+  -- >   displayBuilder = displayList+  --+  -- ==== How implementations are selected+  --+  -- > displayBuilder ([1,2,3] :: [Int])+  -- > → displayBuilder @[Int] = displayBuilderList @Int+  -- > → Default `displayList`+  -- >+  -- > displayBuilder ("abc" :: [Char])+  -- > → displayBuilder @[Char] = displayBuilderList @Char+  -- > → Custom `displayList`+  displayList :: [a] -> Builder+  displayList [] = "[]"+  displayList (x : xs) = "[" <> displayBuilder x <> foldMap go xs <> "]"+    where+      go :: a -> Builder+      go y = "," <> displayBuilder y++  -- | The method 'displayPrec' allows you to write instances that+  -- require nesting. The precedence parameter can be thought of as a+  -- suggestion coming from the surrounding context for how tightly to bind. If the precedence+  -- parameter is higher than the precedence of the operator (or constructor, function, etc.)+  -- being displayed, then that suggests that the output will need to be surrounded in parentheses+  -- in order to bind tightly enough (see 'displayParen').+  --+  -- For example, if an operator constructor is being displayed, then the precedence requirement+  -- for its arguments will be the precedence of the operator. Meaning, if the argument+  -- binds looser than the surrounding operator, then it will require parentheses.+  --+  -- Note that function/constructor application has an effective precedence of 10.+  --+  -- === Examples+  --+  -- > instance (Display a) => Display (Maybe a) where+  -- >   -- In this instance, we define 'displayPrec' rather than 'displayBuilder' as we need to decide+  -- >   -- whether or not to surround ourselves in parentheses based on the surrounding context.+  -- >   -- If the precedence parameter is higher than 10 (the precedence of constructor application)+  -- >   -- then we indeed need to surround ourselves in parentheses to avoid malformed outputs+  -- >   -- such as @Just Just 5@.+  -- >   -- We then set the precedence parameter of the inner 'displayPrec' to 11, as even+  -- >   -- constructor application is not strong enough to avoid parentheses.+  -- >   displayPrec _ Nothing = "Nothing"+  -- >   displayPrec prec (Just a) = displayParen (prec > 10) $ "Just " <> displayPrec 11 a+  --+  -- > data Pair a b = a :*: b+  -- > infix 5 :*: -- arbitrary choice of precedence+  -- > instance (Display a, Display b) => Display (Pair a b) where+  -- >   displayPrec prec (a :*: b) = displayParen (prec > 5) $ displayPrec 6 a <> " :*: " <> displayPrec 6 b+  displayPrec+    :: Int+    -- ^ The precedence level passed in by the surrounding context+    -> a+    -> Builder+  displayPrec _ = displayBuilder++-- | Convert a value to a readable 'Text'.+--+-- === Examples+-- >>> display 3+-- "3"+--+-- >>> display True+-- "True"+--+-- @since 0.0.1.0+display :: Display a => a -> Text+display a = TL.toStrict $ TB.toLazyText $ displayBuilder a++-- | 🚫 You should not try to display functions!+--+-- 💡 Write a 'newtype' wrapper that represents your domain more accurately.+--    If you are not consciously trying to use 'display' on a function,+--    make sure that you are not missing an argument somewhere.+--+-- @since 0.0.1.0+instance CannotDisplayBareFunctions => Display (a -> b) where+  displayBuilder = undefined++-- | @since 0.0.1.0+type family CannotDisplayBareFunctions :: Constraint where+  CannotDisplayBareFunctions =+    TypeError+      ( 'Text "🚫 You should not try to display functions!"+          ':$$: 'Text "💡 Write a 'newtype' wrapper that represents your domain more accurately."+          ':$$: 'Text "   If you are not consciously trying to use `display` on a function,"+          ':$$: 'Text "   make sure that you are not missing an argument somewhere."+      )++-- | 🚫 You should not try to display strict ByteStrings!+--+-- 💡 Always provide an explicit encoding.+-- Use 'Data.Text.Encoding.decodeUtf8'' or 'Data.Text.Encoding.decodeUtf8With' to convert from UTF-8+--+-- @since 0.0.1.0+instance CannotDisplayByteStrings => Display ByteString where+  displayBuilder = undefined++-- | 🚫 You should not try to display lazy ByteStrings!+--+-- 💡 Always provide an explicit encoding.+-- Use 'Data.Text.Encoding.decodeUtf8'' or 'Data.Text.Encoding.decodeUtf8With' to convert from UTF-8+--+-- @since 0.0.1.0+instance CannotDisplayByteStrings => Display BL.ByteString where+  displayBuilder = undefined++type family CannotDisplayByteStrings :: Constraint where+  CannotDisplayByteStrings =+    TypeError+      ( 'Text "🚫 You should not try to display ByteStrings!"+          ':$$: 'Text "💡 Always provide an explicit encoding"+          ':$$: 'Text "Use 'Data.Text.Encoding.decodeUtf8'' or 'Data.Text.Encoding.decodeUtf8With' to convert from UTF-8"+      )++-- | A utility function that surrounds the given 'Builder' with parentheses when the Bool parameter is True.+-- Useful for writing instances that may require nesting. See the 'displayPrec' documentation for more+-- information.+--+-- @since 0.0.1.0+displayParen :: Bool -> Builder -> Builder+displayParen b txt = if b then "(" <> txt <> ")" else txt++-- | This wrapper allows you to create an opaque instance for your type,+-- useful for redacting sensitive content like tokens or passwords.+--+-- === Example+--+-- > data UserToken = UserToken UUID+-- >  deriving Display+-- >    via (OpaqueInstance "[REDACTED]" UserToken)+--+-- > display $ UserToken "7a01d2ce-31ff-11ec-8c10-5405db82c3cd"+-- > "[REDACTED]"+--+-- @since 0.0.1.0+newtype OpaqueInstance (str :: Symbol) (a :: Type) = Opaque a++-- | This wrapper allows you to create an opaque instance for your type,+-- useful for redacting sensitive content like tokens or passwords.+--+-- @since 0.0.1.0+instance KnownSymbol str => Display (OpaqueInstance str a) where+  displayBuilder _ = TB.fromString $ symbolVal (Proxy @str)++-- | This wrapper allows you to rely on a pre-existing 'Show' instance in order to+-- derive 'Display' from it.+--+-- === Example+--+-- > data AutomaticallyDerived = AD+-- >  -- We derive 'Show'+-- >  deriving stock Show+-- >  -- We take advantage of the 'Show' instance to derive 'Display' from it+-- >  deriving Display+-- >    via (ShowInstance AutomaticallyDerived)+--+-- @since 0.0.1.0+newtype ShowInstance (a :: Type)+  = ShowInstance a+  deriving newtype+    ( Show+      -- ^ @since 0.0.1.0+    )++-- | This wrapper allows you to rely on a pre-existing 'Show' instance in order to derive 'Display' from it.+--+-- @since 0.0.1.0+instance Show e => Display (ShowInstance e) where+  displayBuilder s = TB.fromString $ show s++-- @since 0.0.1.0+newtype DisplayDecimal e+  = DisplayDecimal e+  deriving newtype+    (Integral, Real, Enum, Ord, Num, Eq)++-- @since 0.0.1.0+instance Integral e => Display (DisplayDecimal e) where+  displayBuilder = TB.decimal++-- @since 0.0.1.0+newtype DisplayRealFloat e+  = DisplayRealFloat e+  deriving newtype+    (RealFloat, RealFrac, Real, Ord, Eq, Num, Fractional, Floating)++-- @since 0.0.1.0+instance RealFloat e => Display (DisplayRealFloat e) where+  displayBuilder = TB.realFloat++-- | @since 0.0.1.0+deriving via (ShowInstance ()) instance Display ()++-- | @since 0.0.3.0+deriving via (ShowInstance Void) instance Display Void++-- | @since 0.0.1.0+deriving via (ShowInstance Bool) instance Display Bool++-- | @since 0.0.1.0+-- 'displayList' is overloaded, so that when the @Display [a]@ instance calls 'displayList',+-- we end up with a nice string instead of a list of chars between brackets.+--+-- >>> display [1, 2, 3]+-- "[1,2,3]"+--+-- >>> display ['h', 'e', 'l', 'l', 'o']+-- "hello"+instance Display Char where+  -- This instance's implementation is used in the haddocks of the typeclass.+  -- If you change it, reflect the change in the documentation.+  displayBuilder c = TB.fromText $ T.singleton c+  displayList cs = TB.fromText $ T.pack cs++-- | Lazy 'TL.Text'+--+-- @since 0.0.1.0+instance Display TL.Text where+  displayBuilder = TB.fromLazyText++-- | Strict 'Data.Text.Text'+--+-- @since 0.0.1.0+instance Display Text where+  displayBuilder = TB.fromText++-- | @since 0.0.1.0+instance Display a => Display [a] where+  {-# SPECIALIZE instance Display [String] #-}+  {-# SPECIALIZE instance Display [Char] #-}+  {-# SPECIALIZE instance Display [Int] #-}++  -- In this instance, 'displayBuilder' is defined in terms of 'displayList', which for most types+  -- is defined as the default written in the class declaration.+  -- But when @a ~ Char@, there is an explicit implementation that is selected instead, which+  -- provides the rendering of the character string between double quotes.+  displayBuilder = displayList++-- | @since 0.0.1.0+instance Display a => Display (NonEmpty a) where+  displayBuilder (a :| as) = displayBuilder a <> TB.fromString " :| " <> displayBuilder as++-- | @since 0.0.1.0+instance Display a => Display (Maybe a) where+  -- In this instance, we define 'displayPrec' rather than 'displayBuilder' as we need to decide+  -- whether or not to surround ourselves in parentheses based on the surrounding context.+  -- If the precedence parameter is higher than 10 (the precedence of constructor application)+  -- then we indeed need to surround ourselves in parentheses to avoid malformed outputs+  -- such as @Just Just 5@.+  -- We then set the precedence parameter of the inner 'displayPrec' to 11, as even+  -- constructor application is not strong enough to avoid parentheses.+  displayPrec _ Nothing = "Nothing"+  displayPrec prec (Just a) = displayParen (prec > 10) $ "Just " <> displayPrec 11 a++-- | @since 0.0.1.0+deriving via (DisplayRealFloat Double) instance Display Double++-- | @since 0.0.1.0+deriving via (DisplayRealFloat Float) instance Display Float++-- | @since 0.0.1.0+deriving via (DisplayDecimal Int) instance Display Int++-- | @since 0.0.1.0+deriving via (DisplayDecimal Int8) instance Display Int8++-- | @since 0.0.1.0+deriving via (DisplayDecimal Int16) instance Display Int16++-- | @since 0.0.1.0+deriving via (DisplayDecimal Int32) instance Display Int32++-- | @since 0.0.1.0+deriving via (DisplayDecimal Int64) instance Display Int64++-- | @since 0.0.1.0+deriving via (DisplayDecimal Integer) instance Display Integer++-- | @since 0.0.1.0+deriving via (DisplayDecimal Word) instance Display Word++-- | @since 0.0.1.0+deriving via (DisplayDecimal Word8) instance Display Word8++-- | @since 0.0.1.0+deriving via (DisplayDecimal Word16) instance Display Word16++-- | @since 0.0.1.0+deriving via (DisplayDecimal Word32) instance Display Word32++-- | @since 0.0.1.0+deriving via (DisplayDecimal Word64) instance Display Word64++-- | @since 0.0.1.0+deriving via (ShowInstance IOException) instance Display IOException++-- | @since 0.0.1.0+deriving via (ShowInstance SomeException) instance Display SomeException++-- | @since 0.0.1.0+instance (Display a, Display b) => Display (a, b) where+  displayBuilder (a, b) = "(" <> displayBuilder a <> "," <> displayBuilder b <> ")"++-- | @since 0.0.1.0+instance (Display a, Display b, Display c) => Display (a, b, c) where+  displayBuilder (a, b, c) = "(" <> displayBuilder a <> "," <> displayBuilder b <> "," <> displayBuilder c <> ")"++-- | @since 0.0.1.0+instance (Display a, Display b, Display c, Display d) => Display (a, b, c, d) where+  displayBuilder (a, b, c, d) = "(" <> displayBuilder a <> "," <> displayBuilder b <> "," <> displayBuilder c <> "," <> displayBuilder d <> ")"++-- $designChoices+--+-- === A “Lawless Typeclass”+--+-- The 'Display' typeclass does not contain any law. This is a controversial choice for some people,+-- but the truth is that there are not any laws to ask of the consumer that are not already enforced+-- by the type system and the internals of the 'Data.Text.Internal.Text' type.+--+-- === "🚫 You should not try to display functions!"+--+-- Sometimes, when using the library, you may encounter this message:+--+-- > • 🚫 You should not try to display functions!+-- >   💡 Write a 'newtype' wrapper that represents your domain more accurately.+-- >      If you are not consciously trying to use `display` on a function,+-- >      make sure that you are not missing an argument somewhere.+--+-- The 'display' library does not allow the definition and usage of 'Display' on+-- bare function types (@(a -> b)@).+-- Experience and time have shown that due to partial application being baked in the language,+-- many users encounter a partial application-related error message when a simple missing+-- argument to a function is the root cause.+--+-- There may be legitimate uses of a 'Display' instance on a function type.+-- But these usages are extremely dependent on their domain of application.+-- That is why it is best to wrap them in a newtype that can better+-- express and enforce the domain.+--+-- === "🚫 You should not try to display ByteStrings!"+--+-- An arbitrary ByteStrings cannot be safely converted to text without prior knowledge of its encoding.+--+-- As such, in order to avoid dangerously blind conversions, it is recommended to use a specialised+-- function such as 'Data.Text.Encoding.decodeUtf8'' or 'Data.Text.Encoding.decodeUtf8With' if you wish to turn a UTF8-encoded ByteString+-- to Text.
+ src/Data/Text/Display/Generic.hs view
@@ -0,0 +1,168 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE EmptyCase #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- |+--  Module      : Data.Text.Display.Generic+--  Copyright   : © Jonathan Lorimer, 2023+--  License     : MIT+--  Maintainer  : jonathanlorimer@pm.me+--  Stability   : stable+--+--  Generic machinery for automatically deriving display instances for record types+module Data.Text.Display.Generic where++import Data.Kind+import Data.Text.Display.Core+import Data.Text.Lazy.Builder (Builder)+import qualified Data.Text.Lazy.Builder as TB+import Data.Type.Bool+import GHC.Generics+import GHC.TypeLits++-- | Generic typeclass machinery for inducting on the structure+-- of the type, such that we can thread `Display` instances through+-- the structure of the type. The primary use case is for implementing+-- `RecordInstance`, which does this "threading" for record fields. This+-- machinery does, crucially, depend on child types (i.e. the type of a+-- record field) having a `Display` instance.+--+-- @since 0.0.5.0+class GDisplay1 f where+  gdisplayBuilder1 :: f p -> Builder++instance GDisplay1 V1 where+  gdisplayBuilder1 x = case x of {}++instance GDisplay1 U1 where+  gdisplayBuilder1 _ = "()"++-- | This is the most important instance, it can be considered as the "base case". It+-- requires a non-generic `Display` instance. All this generic machinery can be conceptualized+-- as distributing these `displayBuilder` calls across a product type.+instance Display c => GDisplay1 (K1 i c) where+  gdisplayBuilder1 (K1 a) = displayBuilder a++instance (Constructor c, GDisplay1 f) => GDisplay1 (M1 C c f) where+  gdisplayBuilder1 c@(M1 a)+    | conIsRecord c = TB.fromString (conName c) <> "\n  { " <> gdisplayBuilder1 a <> "\n  }"+    | conIsTuple c = TB.fromString (conName c) <> " ( " <> gdisplayBuilder1 a <> " )"+    | otherwise = TB.fromString (conName c) <> " " <> gdisplayBuilder1 a+    where+      conIsTuple :: C1 c f p -> Bool+      conIsTuple y =+        tupleName (conName y)+        where+          tupleName ('(' : ',' : _) = True+          tupleName _ = False++instance (Selector s, GDisplay1 f) => GDisplay1 (M1 S s f) where+  gdisplayBuilder1 s@(M1 a) =+    if selName s == ""+      then gdisplayBuilder1 a+      else TB.fromString (selName s) <> " = " <> gdisplayBuilder1 a++instance GDisplay1 f => GDisplay1 (M1 D s f) where+  gdisplayBuilder1 (M1 a) = gdisplayBuilder1 a++instance (GDisplay1 a, GDisplay1 b) => GDisplay1 (a :*: b) where+  gdisplayBuilder1 (a :*: b) = gdisplayBuilder1 a <> "\n  , " <> gdisplayBuilder1 b++instance (GDisplay1 a, GDisplay1 b) => GDisplay1 (a :+: b) where+  gdisplayBuilder1 (L1 a) = gdisplayBuilder1 a+  gdisplayBuilder1 (R1 b) = gdisplayBuilder1 b++gdisplayBuilderDefault :: (Generic a, GDisplay1 (Rep a)) => a -> Builder+gdisplayBuilderDefault = gdisplayBuilder1 . from++-- | This wrapper allows you to create an `Display` instance for a record,+-- so long as all the record fields have a `Display` instance as well.+--+-- === Example+--+-- > data Password = Password+-- >  deriving Display+-- >    via (OpaqueInstance "[REDACTED]" Password)+--+-- > data MyRecord =+-- >    MyRecord+-- >      { fieldA :: String+-- >      , fieldB :: Maybe String+-- >      , fieldC :: Int+-- >      , pword :: Password+-- >      }+-- >      deriving stock (Generic)+-- >      deriving (Display) via (RecordInstance MyRecord)+--+-- > putStrLn . Data.Text.unpack . display $ MyRecord "hello" (Just "world") 22 Password+--+-- > MyRecord+-- >   { fieldA = hello+-- >   , fieldB = Just world+-- >   , fieldC = 22+-- >   , pword = [REDACTED]+-- >   }+--+-- @since 0.0.5.0+newtype RecordInstance a = RecordInstance {unDisplayProduct :: a}++instance Generic a => Generic (RecordInstance a) where+  type Rep (RecordInstance a) = Rep a+  to = RecordInstance . to+  from (RecordInstance x) = from x++-- | We leverage the `AssertNoSum` type family to prevent consumers+-- from deriving instances for sum types. Sum types should use a manual instance+-- or derive one via `ShowInstance`.+--+-- @since 0.0.5.0+instance (AssertNoSumRecordInstance Display a, Generic a, GDisplay1 (Rep a)) => Display (RecordInstance a) where+  displayBuilder = gdisplayBuilderDefault++-- | This type family is lifted from generic-data. We use it to prevent the user from+-- deriving a `RecordInstance` for sum types+--+-- @since 0.0.5.0+type family HasSum f where+  HasSum V1 = 'False+  HasSum U1 = 'False+  HasSum (K1 i c) = 'False+  HasSum (M1 i c f) = HasSum f+  HasSum (f :*: g) = HasSum f || HasSum g+  HasSum (f :+: g) = 'True++class Assert (pred :: Bool) (msg :: ErrorMessage)+instance Assert 'True msg+instance TypeError msg ~ '() => Assert 'False msg++-- | Constraint to prevent misuse of `RecordInstance` deriving via mechanism.+--+-- === Example+--+-- > data MySum = A | B | C deriving stock (Generic) deriving (Display) via (RecordInstance MySum)+--+-- >    • 🚫 Cannot derive Display instance for MySum via RecordInstance due to sum type+-- >      💡 Sum types should use a manual instance or derive one via ShowInstance.+-- >    • When deriving the instance for (Display MySum)+--+-- @since 0.0.5.0+type AssertNoSumRecordInstance (constraint :: Type -> Constraint) a =+  Assert+    (Not (HasSum (Rep a)))+    ( 'Text "🚫 Cannot derive "+        ':<>: 'ShowType constraint+        ':<>: 'Text " instance for "+        ':<>: 'ShowType a+        ':<>: 'Text " via RecordInstance due to sum type"+        ':$$: 'Text "💡 Sum types should use a manual instance or derive one via ShowInstance."+    )
text-display.cabal view
@@ -1,18 +1,21 @@ cabal-version:      3.0 name:               text-display-version:            0.0.4.0+version:            0.0.5.0 category:           Text synopsis:           A typeclass for user-facing output description:   The 'Display' typeclass provides a solution for user-facing output that does not have to abide by the rules of the Show typeclass. -homepage:           https://hackage.haskell.org/package/text-display/src/docs/book/Introduction.html+homepage:+  https://hackage.haskell.org/package/text-display-0.0.4.0/docs/doc/book/Introduction.html+ bug-reports:        https://github.com/haskell-text/text-display/issues author:             Hécate Moonlight maintainer:         Hécate Moonlight license:            MIT build-type:         Simple-tested-with: GHC == {8.8.4, 8.10.7, 9.0.2, 9.2.7, 9.4.4, 9.6.1}+tested-with:+  GHC ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.7 || ==9.4.5 || ==9.6.1  extra-source-files:   LICENSE@@ -58,7 +61,10 @@   import:          common-extensions   import:          common-ghc-options   hs-source-dirs:  src-  exposed-modules: Data.Text.Display+  exposed-modules:+    Data.Text.Display+    Data.Text.Display.Core+    Data.Text.Display.Generic   build-depends:     , base        >=4.12 && <5.0     , bytestring  >=0.10 && <0.12