{-# LANGUAGE CPP, DataKinds, DeriveGeneric, FlexibleContexts, GeneralizedNewtypeDeriving, KindSignatures, PolyKinds, ScopedTypeVariables, StandaloneDeriving, TypeApplications, TypeFamilies, TypeInType, TypeOperators, UndecidableInstances #-} #if __GLASGOW_HASKELL__ >= 806 {-# LANGUAGE DerivingStrategies #-} #endif #if 806 > __GLASGOW_HASKELL__ import Data.Coerce (Coercible, coerce) #endif import Data.Functor.Contravariant (Contravariant) import Data.Functor.Identity (Identity(..)) import Data.Bifunctor (first, second, bimap) import GHC.Generics (Generic(..)) import qualified GHC.Generics -- Make constructors visible to Coercible import Test.Tasty import Test.Tasty.HUnit import Generic.Data (GShow1) import Generic.Data.Surgery data RowId = RowId deriving Show type InsertId = (InsertField 0 ('Just "pk") RowId :: MajorSurgery k) newtype WithId a = WithId (Data (Operate (Rep a) InsertId) ()) deriving instance GShow1 (Operate (Rep a) InsertId) => Show (WithId a) #if __GLASGOW_HASKELL__ >= 806 deriving newtype instance ( Generic a , Functor (Operate (Rep a) InsertId) , Contravariant (Operate (Rep a) InsertId) ) => Generic (WithId a) #else -- Without DerivingStrategies, we do newtype deriving of Generic by hand. instance ( Generic a , Functor (Operate (Rep a) InsertId) , Contravariant (Operate (Rep a) InsertId) ) => Generic (WithId a) where type Rep (WithId a) = Operate (Rep a) InsertId to = to' from = from' to' :: forall a x. (Coercible a (Data (Rep a) ()), Functor (Rep a), Contravariant (Rep a)) => Rep a x -> a to' = coerce (to @(Data (Rep a) ()) @x) from' :: forall a x. (Coercible a (Data (Rep a) ()), Functor (Rep a), Contravariant (Rep a)) => a -> Rep a x from' = coerce (from @(Data (Rep a) ()) @x) #endif addKey :: ( Generic a , Perform (Rep a) InsertId ) => RowId -> a -> WithId a addKey i = WithId . fromOR' . insertRField' @"pk" @0 @RowId i . toOR data Woof = Waf { fluff :: Int } deriving Generic type SemiFluff = RemoveRField "fluff" Int type Fluffy = (SemiFluff :>> InsertField 0 ('Just "fluffy") Bool :: MajorSurgery k) unfluff :: (Generic a, Perform (Rep a) SemiFluff) => a -> (Int, Data (Operate (Rep a) SemiFluff) ()) unfluff = fmap fromOR' . removeRField @"fluff" . toOR fluffier :: (Generic a, Perform (Rep a) Fluffy) => a -> Data (Operate (Rep a) Fluffy) () fluffier = fromOR' . insertRField @"fluffy" @0 . first (>= 0) . removeRField @"fluff" . toOR data Meow = Miaou Int deriving Generic type UnMiaou = (RemoveConstr "Miaou" (Identity Int) :: MajorSurgery k) type Paw = InsertConstrAt "Paw" 1 (Bool, Bool) unMiaou :: (Generic a, Perform (Rep a) UnMiaou) => a -> Either Int (Data (Operate (Rep a) UnMiaou) ()) unMiaou = bimap runIdentity fromOR' . removeConstrT @"Miaou" . toOR purr :: (Generic a, Perform (Rep a) Paw) => Either (Bool, Bool) a -> Data (Operate (Rep a) Paw) () purr = fromOR' . insertConstrT @"Paw" @1 . second toOR type Aww = (Paw :>> UnMiaou :: MajorSurgery k) {- pat :: forall a. (Generic a, Perform (Rep a) Aww) => Either (Bool, Bool) a -> Either (Identity Int) (Data (Operate (Rep a) Aww) ()) pat = second fromOR' . removeConstrT @"Miaou" . insertConstrT @"Paw" @1 @(Bool, Bool) . second toOR -} main :: IO () main = defaultMain test test :: TestTree test = testGroup "synthetic" [ testCase "addKey" $ "WithId (Waf {pk = RowId, fluff = 77})" @?= show (addKey RowId (Waf 77)) , testCase "unfluff" $ "(33,Waf {})" @?= show (unfluff (Waf 33)) , testCase "fluffier" $ "Waf {fluffy = True}" @?= show (fluffier (Waf 33)) , testCase "unMiaou" $ "Left 3" @?= show (unMiaou (Miaou 3)) , testCase "purr" $ "Miaou 3" @?= show (purr (Right (Miaou 3))) ]