-----------------------------------------------------------------------------
-- |
-- Module      :  Data.Barbie
--
-- A common Haskell idiom is to parameterise a datatype by a type @* -> *@,
-- typically a functor or a GADT. These are like outfits of a Barbie,
-- that turn her into a different doll. E.g.
--
-- @
-- data Barbie f
--   = Barbie
--       { name :: f 'String'
--       , age  :: f 'Int'
--       }
--
-- b1 :: Barbie 'Data.Monoid.Last'       -- Barbie with a monoid structure
-- b2 :: Barbie ('Data.Functor.Const.Const' a)  -- 'Data.Barbie.Container.Container' Barbie
-- b3 :: Barbie 'Data.Functor.Identity.Identity'   -- Barbie's new clothes
-- @
--
-- This module define the classes to work with these types and easily
-- transform them. They all come with default instances based on
-- `GHC.Generics.Generic`, so using them is as easy as:
--
-- @
-- data Barbie f
--   = Barbie
--       { name :: f 'String'
--       , age  :: f 'Int'
--       }
--   deriving
--     ( 'GHC.Generics.Generic'
--     , 'FunctorB', 'TraversableB', 'ProductB', 'ConstraintsB', 'ProofB'
--     )
--
-- deriving instance 'ConstraintsOf' 'Show' f Barbie => 'Show' Barbie
-- deriving instance 'ConstraintsOf' 'Eq'   f Barbie => 'Eq'   Barbie
-- @
--
-- Sometimes one wants to use @Barbie 'Data.Functor.Identity.Identity'@
-- and it may feels lik a second-class record type, where one needs to
-- unpack values in each field. For those cases, we can leverage on
-- closed type-families ang get the best of both worlds:
--
-- @
-- data 'Bare'
--
-- type family 'Wear' f a where
--   'Wear' 'Bare' a = a
--   'Wear' f      a = f a
--
-- data SignUpForm f
--   = SignUpForm'
--       { username  :: 'Wear' f 'String',
--       , password  :: 'Wear' f 'String'
--       , mailingOk :: 'Wear' f 'Boolean'
--       }
--   deriving ( ..., 'BareB')
--
-- type SignUpRaw  = SignUpForm 'Maybe'
-- type SignUpData = SignUpForm 'Bare'
--
-- formData = SignUpForm "jbond" "shaken007" False :: SignUpData
-- @


----------------------------------------------------------------------------
module Data.Barbie
  (
    -- * Functor
    FunctorB(bmap)

    -- * Traversable
  , TraversableB(btraverse)
  , bsequence

    -- * Product
  , ProductB(buniq, bprod)
  , (/*/), (/*)
  , bzip, bunzip, bzipWith, bzipWith3, bzipWith4

    -- * Bare values
  , Wear
  , Bare
  , BareB(bstrip, bcover)
  , bstripFrom
  , bcoverWith

    -- * Constraints and proofs of instance
  , ConstraintsB(ConstraintsOf, adjProof)
  , ProofB(bproof)

    -- * Wrapper
  , Barbie(..)
  )

where

import Data.Barbie.Internal.Bare(Bare, BareB(..), bstripFrom, bcoverWith, Wear)
import Data.Barbie.Internal.Constraints(ConstraintsB(..))
import Data.Barbie.Internal.Functor(FunctorB(..))
import Data.Barbie.Internal.Instances(Barbie(..))
import Data.Barbie.Internal.ProofB(ProofB(..))
import Data.Barbie.Internal.Product
  ( ProductB(..)
  , bzip, bunzip, bzipWith, bzipWith3, bzipWith4
  , (/*/), (/*)
  )
import Data.Barbie.Internal.Traversable(TraversableB(..), bsequence)