packages feed

large-generics-0.1.0.0: test/Test/Record/Generic/Infra/Examples.hs

{-# LANGUAGE ConstraintKinds         #-}
{-# LANGUAGE DataKinds               #-}
{-# LANGUAGE DeriveGeneric           #-}
{-# LANGUAGE FlexibleContexts        #-}
{-# LANGUAGE FlexibleInstances       #-}
{-# LANGUAGE InstanceSigs            #-}
{-# LANGUAGE MultiParamTypeClasses   #-}
{-# LANGUAGE RecordWildCards         #-}
{-# LANGUAGE ScopedTypeVariables     #-}
{-# LANGUAGE StandaloneDeriving      #-}
{-# LANGUAGE TypeApplications        #-}
{-# LANGUAGE TypeFamilies            #-}
{-# LANGUAGE UndecidableInstances    #-}
{-# LANGUAGE UndecidableSuperClasses #-}
{-# LANGUAGE ViewPatterns            #-}

{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}

-- | Standard Haskell records with a hand-written Generic instance
--
-- @large-records@ and @large-anon@ provide different record representations,
-- but here we want to test the generics infrastructure independent from these
-- libraries. The definitions here are simple, not intended to check for core
-- size; we do that in large-records and large-anon.
--
-- These definitions serve as a simple example of how the 'Generic' class might
-- be instantiated, and are used as a basis for testing.
module Test.Record.Generic.Infra.Examples (
    -- * Simple
    SimpleRecord(..)
  , exampleSimpleRecord
    -- * With type parameters
  , ParamRecord(..)
  , exampleParamRecord
    -- * Higher-kinded
  , Regular(..)
  , Irregular(..)
  , MultiFun(..)
  , exampleRegular
  , exampleIrregular
    -- * Beam-like
  , MixinTable(..)
  , FullTable(..)
  , PrimaryKey(..)
  , exampleMixinTable
  ) where

import Data.Kind

import qualified GHC.Generics as GHC
import qualified Generics.SOP as SOP

import Test.QuickCheck

import Data.Record.Generic
import Data.Record.Generic.Rep.Internal (noInlineUnsafeCo)

import qualified Data.Record.Generic.Rep.Internal as Rep

import Test.Record.Generic.Infra.Beam.Mini
import Data.Functor.Identity

{-------------------------------------------------------------------------------
  Simple record
-------------------------------------------------------------------------------}

data SimpleRecord = MkSimpleRecord {
      simpleRecordField1 :: Word
    , simpleRecordField2 :: Bool
    }
  deriving (Show, Eq, GHC.Generic) -- GHC generics for the GhcGenerics tests

exampleSimpleRecord :: SimpleRecord
exampleSimpleRecord = MkSimpleRecord 5 True

class    (c Word, c Bool) => ConstraintsSimpleRecord c where
instance (c Word, c Bool) => ConstraintsSimpleRecord c where

instance Generic SimpleRecord where
  type Constraints SimpleRecord = ConstraintsSimpleRecord
  type MetadataOf  SimpleRecord = '[ '("simpleRecordField1", Word)
                                   , '("simpleRecordField2", Bool)
                                   ]

  from MkSimpleRecord{..} = Rep.unsafeFromListAny [
        I $ noInlineUnsafeCo simpleRecordField1
      , I $ noInlineUnsafeCo simpleRecordField2
      ]

  to rep =
    let { [ I (noInlineUnsafeCo -> simpleRecordField1)
          , I (noInlineUnsafeCo -> simpleRecordField2)
          ] = Rep.toListAny rep }
    in MkSimpleRecord{..}

  dict :: forall c.
       ConstraintsSimpleRecord c
    => Proxy c -> Rep (Dict c) SimpleRecord
  dict _ = Rep.unsafeFromListAny [
        noInlineUnsafeCo (Dict :: Dict c Word)
      , noInlineUnsafeCo (Dict :: Dict c Bool)
      ]

  metadata _ = Metadata {
        recordName          = "SimpleRecord"
      , recordConstructor   = "MkSimpleRecord"
      , recordSize          = 2
      , recordFieldMetadata = Rep.unsafeFromListAny [
            noInlineUnsafeCo $
              FieldMetadata (Proxy @"simpleRecordField1") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"simpleRecordField2") FieldLazy
          ]
      }

instance Arbitrary SimpleRecord where
  arbitrary = MkSimpleRecord <$> arbitrary <*> arbitrary
  shrink (MkSimpleRecord f1 f2) = concat [
        [ MkSimpleRecord f1' f2
        | f1' <- shrink f1
        ]
      , [ MkSimpleRecord f1 f2'
        | f2' <- shrink f2
        ]
      ]

{-------------------------------------------------------------------------------
  Record with some type arguments (but not higher-kinded)
-------------------------------------------------------------------------------}

data ParamRecord a b = MkParamRecord {
      paramRecordField1 :: Word
    , paramRecordField2 :: Bool
    , paramRecordField3 :: Char
    , paramRecordField4 :: a
    , paramRecordField5 :: [b]
    }
  deriving (Eq, Ord, Show, GHC.Generic)

instance SOP.Generic (ParamRecord a b)
  -- For comparison purposes only

class    (c Word, c Bool, c Char, c a, c [b]) => ConstraintsParamRecord a b c
instance (c Word, c Bool, c Char, c a, c [b]) => ConstraintsParamRecord a b c

instance Generic (ParamRecord a b) where
  type Constraints (ParamRecord a b) = ConstraintsParamRecord a b
  type MetadataOf  (ParamRecord a b) = '[ '("paramRecordField1", Word)
                                        , '("paramRecordField2", Bool)
                                        , '("paramRecordField3", Char)
                                        , '("paramRecordField4", a)
                                        , '("paramRecordField5", [b])
                                        ]

  from MkParamRecord{..} = Rep.unsafeFromListAny [
        I $ noInlineUnsafeCo paramRecordField1
      , I $ noInlineUnsafeCo paramRecordField2
      , I $ noInlineUnsafeCo paramRecordField3
      , I $ noInlineUnsafeCo paramRecordField4
      , I $ noInlineUnsafeCo paramRecordField5
      ]

  to rep =
    let { [ I (noInlineUnsafeCo -> paramRecordField1)
          , I (noInlineUnsafeCo -> paramRecordField2)
          , I (noInlineUnsafeCo -> paramRecordField3)
          , I (noInlineUnsafeCo -> paramRecordField4)
          , I (noInlineUnsafeCo -> paramRecordField5)
          ] = Rep.toListAny rep }
    in MkParamRecord{..}

  dict :: forall c.
       ConstraintsParamRecord a b c
    => Proxy c -> Rep (Dict c) (ParamRecord a b)
  dict _ = Rep.unsafeFromListAny [
        noInlineUnsafeCo (Dict :: Dict c Word)
      , noInlineUnsafeCo (Dict :: Dict c Bool)
      , noInlineUnsafeCo (Dict :: Dict c Char)
      , noInlineUnsafeCo (Dict :: Dict c a)
      , noInlineUnsafeCo (Dict :: Dict c [b])
      ]

  metadata _ = Metadata {
        recordName          = "ParamRecord"
      , recordConstructor   = "MkParamRecord"
      , recordSize          = 5
      , recordFieldMetadata = Rep.unsafeFromListAny [
            noInlineUnsafeCo $
              FieldMetadata (Proxy @"paramRecordField1") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"paramRecordField2") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"paramRecordField3") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"paramRecordField4") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"paramRecordField5") FieldLazy
          ]
      }

exampleParamRecord :: ParamRecord () Float
exampleParamRecord = MkParamRecord 5 True 'c' () [3.14]

{-------------------------------------------------------------------------------
  Higher kinded record: regular
-------------------------------------------------------------------------------}

-- | Regular example: all fields have an @f@ parameter
data Regular f = MkRegular {
        regularField1 :: f Int
      , regularField2 :: f Bool
      , regularField3 :: f String
      }

exampleRegular :: Regular I
exampleRegular = MkRegular {
      regularField1 = I 5
    , regularField2 = I True
    , regularField3 = I "a"
    }

deriving instance (Show (f Int), Show (f Bool), Show (f String)) => Show (Regular f)
deriving instance (Eq   (f Int), Eq   (f Bool), Eq   (f String)) => Eq   (Regular f)

class    (c (f Int), c (f Bool), c (f String)) => ConstraintsRegular f c
instance (c (f Int), c (f Bool), c (f String)) => ConstraintsRegular f c

instance Generic (Regular f) where
  type Constraints (Regular f) = ConstraintsRegular f
  type MetadataOf  (Regular f) = '[ '("regularField1", f Int)
                                  , '("regularField1", f Bool)
                                  , '("regularField3", f String)
                                  ]


  from MkRegular{..} = Rep.unsafeFromListAny [
        I $ noInlineUnsafeCo regularField1
      , I $ noInlineUnsafeCo regularField2
      , I $ noInlineUnsafeCo regularField3
      ]

  to rep =
    let { [ I (noInlineUnsafeCo -> regularField1)
          , I (noInlineUnsafeCo -> regularField2)
          , I (noInlineUnsafeCo -> regularField3)
          ] = Rep.toListAny rep }
    in MkRegular{..}

  dict :: forall c.
       ConstraintsRegular f c
    => Proxy c -> Rep (Dict c) (Regular f)
  dict _ = Rep.unsafeFromListAny [
        noInlineUnsafeCo (Dict :: Dict c (f Int))
      , noInlineUnsafeCo (Dict :: Dict c (f Bool))
      , noInlineUnsafeCo (Dict :: Dict c (f String))
      ]

  metadata _ = Metadata {
        recordName          = "Regular"
      , recordConstructor   = "MkRegular"
      , recordSize          = 3
      , recordFieldMetadata = Rep.unsafeFromListAny [
            noInlineUnsafeCo $
              FieldMetadata (Proxy @"regularField1") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"regularField2") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"regularField3") FieldLazy
          ]
      }

{-------------------------------------------------------------------------------
  Higher kinded record: irregular
-------------------------------------------------------------------------------}

-- | Irregular example: not all fields have an @f@ parameter
data Irregular f = MkIrregular {
        irregularField1 :: f Int
      , irregularField2 :: f Bool
      , irregularField3 :: String
      }

exampleIrregular :: Irregular I
exampleIrregular = MkIrregular {
      irregularField1 = I 1234
    , irregularField2 = I True
    , irregularField3 = "hi"
    }

deriving instance (Show (f Int), Show (f Bool)) => Show (Irregular f)
deriving instance (Eq   (f Int), Eq   (f Bool)) => Eq   (Irregular f)

class    (c (f Int), c (f Bool), c String) => ConstraintsIrregular f c
instance (c (f Int), c (f Bool), c String) => ConstraintsIrregular f c

instance Generic (Irregular f) where
  type Constraints (Irregular f) = ConstraintsIrregular f
  type MetadataOf  (Irregular f) = '[ '("irregularField1", f Int)
                                    , '("irregularField1", f Bool)
                                    , '("irregularField3", String)
                                    ]

  from MkIrregular{..} = Rep.unsafeFromListAny [
        I $ noInlineUnsafeCo irregularField1
      , I $ noInlineUnsafeCo irregularField2
      , I $ noInlineUnsafeCo irregularField3
      ]

  to rep =
    let { [ I (noInlineUnsafeCo -> irregularField1)
          , I (noInlineUnsafeCo -> irregularField2)
          , I (noInlineUnsafeCo -> irregularField3)
          ] = Rep.toListAny rep }
    in MkIrregular{..}

  dict :: forall c.
       ConstraintsIrregular f c
    => Proxy c -> Rep (Dict c) (Irregular f)
  dict _ = Rep.unsafeFromListAny [
        noInlineUnsafeCo (Dict :: Dict c (f Int))
      , noInlineUnsafeCo (Dict :: Dict c (f Bool))
      , noInlineUnsafeCo (Dict :: Dict c String)
      ]

  metadata _ = Metadata {
        recordName          = "Irregular"
      , recordConstructor   = "MkIrregular"
      , recordSize          = 3
      , recordFieldMetadata = Rep.unsafeFromListAny [
            noInlineUnsafeCo $
              FieldMetadata (Proxy @"irregularField1") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"irregularField2") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"irregularField3") FieldLazy
          ]
      }

{-------------------------------------------------------------------------------
  Higher kinded record: multiple functors
-------------------------------------------------------------------------------}

data MultiFun f g = MkMultiFun {
        multiFunField1 :: f Int
      , multiFunField2 :: g Bool
      , multiFunField3 :: String
      }

deriving instance (Show (f Int), Show (g Bool)) => Show (MultiFun f g)
deriving instance (Eq   (f Int), Eq   (g Bool)) => Eq   (MultiFun f g)

class    (c (f Int), c (g Bool), c String) => ConstraintsMultiFun f g c
instance (c (f Int), c (g Bool), c String) => ConstraintsMultiFun f g c

instance Generic (MultiFun f g) where
  type Constraints (MultiFun f g) = ConstraintsMultiFun f g
  type MetadataOf  (MultiFun f g) = '[ '("multiFunField1", f Int)
                                     , '("multiFunField1", g Bool)
                                     , '("multiFunField3", String)
                                     ]

  from MkMultiFun{..} = Rep.unsafeFromListAny [
        I $ noInlineUnsafeCo multiFunField1
      , I $ noInlineUnsafeCo multiFunField2
      , I $ noInlineUnsafeCo multiFunField3
      ]

  to rep =
    let { [ I (noInlineUnsafeCo -> multiFunField1)
          , I (noInlineUnsafeCo -> multiFunField2)
          , I (noInlineUnsafeCo -> multiFunField3)
          ] = Rep.toListAny rep }
    in MkMultiFun{..}

  dict :: forall c.
       ConstraintsMultiFun f g c
    => Proxy c -> Rep (Dict c) (MultiFun f g)
  dict _ = Rep.unsafeFromListAny [
        noInlineUnsafeCo (Dict :: Dict c (f Int))
      , noInlineUnsafeCo (Dict :: Dict c (g Bool))
      , noInlineUnsafeCo (Dict :: Dict c String)
      ]

  metadata _ = Metadata {
        recordName          = "MultiFun"
      , recordConstructor   = "MkMultiFun"
      , recordSize          = 3
      , recordFieldMetadata = Rep.unsafeFromListAny [
            noInlineUnsafeCo $
              FieldMetadata (Proxy @"multiFunField1") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"multiFunField2") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"multiFunField3") FieldLazy
          ]
      }

{-------------------------------------------------------------------------------
  Beam-like mixin table

  This is the simpler case, because it contains only Columnar fields.
-------------------------------------------------------------------------------}

data MixinTable (f :: Type -> Type) = MkMixinTable {
      mixinTableField1 :: Columnar f Char
    , mixinTableField2 :: Columnar f Double
    }

exampleMixinTable :: MixinTable Identity
exampleMixinTable = MkMixinTable {
      mixinTableField1 = 'a'
    , mixinTableField2 = 3.14
    }

deriving instance ( Show (Columnar f Char)
                  , Show (Columnar f Double)
                  ) => Show (MixinTable f)
deriving instance ( Eq (Columnar f Char)
                  , Eq (Columnar f Double)
                  ) => Eq (MixinTable f)

class    (c (Columnar f Char), c (Columnar f Double)) => ConstraintsMixinTable f c
instance (c (Columnar f Char), c (Columnar f Double)) => ConstraintsMixinTable f c

instance Generic (MixinTable f) where
  type Constraints (MixinTable f) = ConstraintsMixinTable f
  type MetadataOf  (MixinTable f) = '[ '("mixinTableField1", Columnar f Char)
                                     , '("mixinTableField2", Columnar f Double)
                                     ]

  from MkMixinTable{..} = Rep.unsafeFromListAny [
        I $ noInlineUnsafeCo mixinTableField1
      , I $ noInlineUnsafeCo mixinTableField2
      ]

  to rep =
    let { [ I (noInlineUnsafeCo -> mixinTableField1)
          , I (noInlineUnsafeCo -> mixinTableField2)
          ] = Rep.toListAny rep }
    in MkMixinTable{..}

  dict :: forall c.
       ConstraintsMixinTable f c
    => Proxy c -> Rep (Dict c) (MixinTable f)
  dict _ = Rep.unsafeFromListAny [
        noInlineUnsafeCo (Dict :: Dict c (Columnar f Char))
      , noInlineUnsafeCo (Dict :: Dict c (Columnar f Double))
      ]

  metadata _ = Metadata {
        recordName          = "MixinTable"
      , recordConstructor   = "MkMixinTable"
      , recordSize          = 2
      , recordFieldMetadata = Rep.unsafeFromListAny [
            noInlineUnsafeCo $
              FieldMetadata (Proxy @"mixinTableField1") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"mixinTableField2") FieldLazy
          ]
      }

{-------------------------------------------------------------------------------
  Beam-like full table example
-------------------------------------------------------------------------------}

data FullTable (f :: Type -> Type) = MkFullTable {
      fullTableField1 :: PrimaryKey FullTable f
    , fullTableField2 :: Columnar f Bool
    , fullTableField3 :: MixinTable f
    }

data instance PrimaryKey FullTable f = PrimA (Columnar f Int)

deriving instance Show (Columnar f Int) => Show (PrimaryKey FullTable f)
deriving instance Eq   (Columnar f Int) => Eq   (PrimaryKey FullTable f)

deriving instance ( Show (Columnar f Int)
                  , Show (Columnar f Bool)
                  , Show (Columnar f Char)
                  , Show (Columnar f Double)
                  ) => Show (FullTable f)
deriving instance ( Eq (Columnar f Int)
                  , Eq (Columnar f Bool)
                  , Eq (Columnar f Char)
                  , Eq (Columnar f Double)
                  ) => Eq (FullTable f)

class    ( c (PrimaryKey FullTable f)
         , c (Columnar f Bool)
         , c (MixinTable f)
         ) => ConstraintsFullTable f c
instance ( c (PrimaryKey FullTable f)
         , c (Columnar f Bool)
         , c (MixinTable f)
         ) => ConstraintsFullTable f c

instance Generic (FullTable f) where
  type Constraints (FullTable f) = ConstraintsFullTable f
  type MetadataOf  (FullTable f) = '[ '("fullTableField1", PrimaryKey FullTable f)
                                    , '("fullTableField2", Columnar f Bool)
                                    , '("fullTableField3", MixinTable f)
                                    ]

  from MkFullTable{..} = Rep.unsafeFromListAny [
        I $ noInlineUnsafeCo fullTableField1
      , I $ noInlineUnsafeCo fullTableField2
      , I $ noInlineUnsafeCo fullTableField3
      ]

  to rep =
    let { [ I (noInlineUnsafeCo -> fullTableField1)
          , I (noInlineUnsafeCo -> fullTableField2)
          , I (noInlineUnsafeCo -> fullTableField3)
          ] = Rep.toListAny rep }
    in MkFullTable{..}

  dict :: forall c.
       ConstraintsFullTable f c
    => Proxy c -> Rep (Dict c) (FullTable f)
  dict _ = Rep.unsafeFromListAny [
        noInlineUnsafeCo (Dict :: Dict c (PrimaryKey FullTable f))
      , noInlineUnsafeCo (Dict :: Dict c (Columnar f Bool))
      , noInlineUnsafeCo (Dict :: Dict c (MixinTable f))
      ]

  metadata _ = Metadata {
        recordName          = "FullTable"
      , recordConstructor   = "MkFullTable"
      , recordSize          = 3
      , recordFieldMetadata = Rep.unsafeFromListAny [
            noInlineUnsafeCo $
              FieldMetadata (Proxy @"fullTableField1") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"fullTableField2") FieldLazy
          , noInlineUnsafeCo $
              FieldMetadata (Proxy @"fullTableField3") FieldLazy
          ]
      }