{-# LANGUAGE EmptyDataDecls #-}

-- | Defines the extra types we use when representing algebraic data in parallel arrays.
--   We don't store values of user defined algebraic type directly in PArrays. Instead,
--   we convert these to a generic representation and store that representation.
--
--   Conversion to and from the generic representation is handled by the methods
--   of the PA class defined in "Data.Array.Parallel.PArray.PRepr".
--
---  For further information see:
--     "Instant Generics: Fast and Easy", Chakravarty, Ditu and Keller, 2009
-- 
module Data.Array.Parallel.PArray.Types (
  -- * The Void type
  Void,
  void,
  fromVoid,     

  -- * Generic sums
  Sum2(..),
  Sum3(..),

  -- * The Wrap type
  Wrap (..)
)
where

-- Void -----------------------------------------------------------------------
-- | The `Void` type is used when representing enumerations. 
--   A type like Bool is represented as @Sum2 Void Void@, meaning that we only
--   only care about the tag of the data constructor and not its argumnent.
data Void

-- | A 'value' with the void type. Used as a placholder like `undefined`.
--   Forcing this yields `error`. 
void    :: Void
void     = error $ unlines
         [ "Data.Array.Parallel.PArray.Types.void"
         , "  With the DPH generic array representation, values of type void"
         , "  should never be forced. Something has gone badly wrong." ]


-- | Coerce a `Void` to a different type. Used as a placeholder like `undefined`.
--   Forcing the result yields `error`.
fromVoid :: a
fromVoid = error $unlines
         [ "Data.Array.Parallel.PArray.Types.fromVoid"
         , "  With the DPH generic array representation, values of type void"
         , "  should never be forced. Something has gone badly wrong." ]


-- Sums -----------------------------------------------------------------------
-- | Sum types used for the generic representation of algebraic data.
data Sum2 a b   = Alt2_1 a | Alt2_2 b
data Sum3 a b c = Alt3_1 a | Alt3_2 b | Alt3_3 c


-- Wrap -----------------------------------------------------------------------
-- | When converting a data type to its generic representation, we use
--   `Wrap` to help us convert only one layer at a time. For example:
--
--   @
--   data Foo a = Foo Int a
--
--   instance PA a => PA (Foo a) where
--    type PRepr (Foo a) = (Int, Wrap a)  -- define how (Foo a) is represented
--   @
--
--   Here we've converted the @Foo@ data constructor to a pair, and Int
--   is its own representation type. We have PData/PR instances for pairs and
--   Ints, so we can work with arrays of these types. However, we can't just
--   use (Int, a) as the representation of (Foo a) because 'a' might
--   be user defined and we won't have PData/PR instances for it.
--
--   Instead, we wrap the second element with the Wrap constructor, which tells
--   us that if we want to process this element we still need to convert it
--   to the generic representation (and back). This last part is done by
--   the PR instance of Wrap, who's methods are defined by calls to the *PD 
--   functions from "Data.Array.Parallel.PArray.PRepr".
--
newtype Wrap a = Wrap { unWrap :: a }