{- |

I find it particularly embarrassing when details of a programming language leak
into a user interface, like when you see the word @undefined@ appear in a web
application. Statically typed languages are not entirely immune to this sort of
bug. In Haskell it's easy to make this mistake when you use 'show'.

For example, let's say we're showing an integer @i@:

> let i = 5 :: Integer
> displayInTheUI ("The number is " <> Text.pack (show i))

and we later refactor the code such that @i@ is now optional:

> let i = Just 5 :: Maybe Integer
> displayInTheUI ("The number is " <> Text.pack (show i))

We've forgotten to update what we show the user, but unfortunately it still
compiles, our user sees the text "Just 5", and their illusion that we can write
flawless code in Haskell is shattered.

Instead we should ensure we're showing them human text:

> let i = 5 :: Integer
> displayInTheUI ("The number is " <> humanText i)

and this does not compile:

> let i = Just 5 :: Maybe Integer
> displayInTheUI ("The number is " <> humanText i)

-}

module Data.Text.Human
    (
    -- * The @HumanText@ class
      HumanText (..)

    -- * Instances

    -- ** Numbers
    -- $numbers

    -- ** Either
    -- $either
    ) where

import Data.Either (Either (..))
import Data.Int (Int, Int8, Int16, Int32, Int64)
import Data.Text (Text, pack)
import Data.Void (Void, absurd)
import Numeric.Natural (Natural)
import Prelude (Integer, show)

-- | An entirely unprincipled class for things which can be converted to text
-- that might be suitable to show to a human user.
class HumanText a where

    -- | Convert a value to some text suitable for displaying to a person. The
    -- output should probably not include Haskell or JSON or any other nerd
    -- shit.
    humanText :: a -> Text


--------------------------------------------------------------------------------
--  Void
--------------------------------------------------------------------------------

instance HumanText Void where
    humanText = absurd


--------------------------------------------------------------------------------
--  Either
--------------------------------------------------------------------------------

{- $either

If @a@ and @b@ both have human text representations, then the human text for
@'Either' a b@ is simply @humanText a@ or @humanText b@. Note that the output
won't necessarily reflect whether the value was 'Left' or 'Right'.

-}

instance (HumanText a, HumanText b) => HumanText (Either a b) where
    humanText (Left  a) = humanText a
    humanText (Right b) = humanText b


--------------------------------------------------------------------------------
--  Numbers
--------------------------------------------------------------------------------

{- $numbers

The integer types have 'HumanText' instances. The floating-point numbers don't,
because 0.1 + 0.2 = 0.30000000000000004 isn't human-friendly.

-}

instance HumanText Int where
    humanText i = pack (show i)

instance HumanText Int8 where
    humanText i = pack (show i)

instance HumanText Int16 where
    humanText i = pack (show i)

instance HumanText Int32 where
    humanText i = pack (show i)

instance HumanText Int64 where
    humanText i = pack (show i)

instance HumanText Integer where
    humanText i = pack (show i)

instance HumanText Natural where
    humanText i = pack (show i)