{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.NamedSOP.Generic
( genProduct
, specProduct
, genSum
, specSum
, GenProduct(GProduct)
, GenSum(GSum)
) where
import Data.Kind
import Data.Singletons
import Data.Singletons.Prelude.Monoid
import Data.Singletons.Prelude.Num
import Data.Singletons.Prelude.Show
import Data.Singletons.TypeLits
import GHC.Generics
import Data.NamedSOP.Map
import Data.NamedSOP.Sum
class GenProduct (f :: * -> *) where
type GProduct f :: [Mapping Symbol Type]
genProduct' :: f a -> NMap (GProduct f)
specProduct' :: NMap (GProduct f) -> f a
instance GenProduct f => GenProduct (D1 _a f) where
type GProduct (D1 _a f) = GProduct f
genProduct' = genProduct' . unM1
specProduct' = M1 . specProduct'
instance GenProduct f => GenProduct (C1 ('MetaCons _a _b 'True) f) where
type GProduct (C1 ( 'MetaCons _a _b 'True) f) = GProduct f
genProduct' = genProduct' . unM1
specProduct' = M1 . specProduct'
instance GenProduct (S1 ('MetaSel ('Just n) _a _b _c) (Rec0 t)) where
type GProduct (S1 ( 'MetaSel ( 'Just n) _a _b _c) (Rec0 t)) = '[n ':-> t]
genProduct' (M1 (K1 c)) = NMapExt c NMapEmpty
specProduct' (NMapExt c NMapEmpty) = M1 (K1 c)
instance (SingI (GProduct f), SingI (GProduct g),
GenProduct f, GenProduct g) => GenProduct (f :*: g) where
type GProduct (f :*: g) = Union (GProduct f) (GProduct g)
genProduct' (x :*: y) = unionMap (genProduct' x, genProduct' y)
specProduct' m =
let (m1, m2) = ununionMap m in specProduct' m1 :*: specProduct' m2
instance GenProductN f => GenProduct (C1 ('MetaCons _a _b 'False) f) where
type GProduct (C1 ( 'MetaCons _a _b 'False) f) = GProductN 1 f
genProduct' (M1 x) = genProductN' (SNat @1) x
specProduct' x = M1 (specProductN' (SNat @1) x)
class GenProductN (f :: * -> *) where
type GProductN (n :: Nat) f :: [Mapping Symbol Type]
type GProductS f :: Nat
sGProductS :: SNat (GProductS f)
sGProductN :: Sing n -> Sing (GProductN n f)
genProductN' :: Sing n -> f a -> NMap (GProductN n f)
specProductN' :: Sing n -> NMap (GProductN n f) -> f a
instance GenProductN (S1 ('MetaSel 'Nothing _a _b _c) (Rec0 t)) where
type GProductN n (S1 ( 'MetaSel 'Nothing _a _b _c) (Rec0 t)) = '[Mappend "_" (Show_ n) ':-> t]
type GProductS (S1 ( 'MetaSel 'Nothing _a _b _c) (Rec0 t)) = 1
sGProductS = SNat
sGProductN sn = SCons (SMapping (sMappend (SSym @"_") (sShow_ sn))) SNil
genProductN' _ (M1 (K1 c)) = NMapExt c NMapEmpty
specProductN' _ (NMapExt c NMapEmpty) = M1 (K1 c)
instance (GenProductN f, GenProductN g) => GenProductN (f :*: g) where
type GProductN n (f :*: g)
= Union (GProductN n f) (GProductN (n + GProductS f) g)
type GProductS (f :*: g) = GProductS f + GProductS g
sGProductS = sGProductS @f %+ sGProductS @g
sGProductN sn =
sUnion (sGProductN @f sn) (sGProductN @g (sn %+ sGProductS @f))
genProductN' sn (x :*: y) =
let (m1 , m2 ) = (genProductN' sn x, genProductN' (sn %+ sGProductS @f) y)
(sm1, sm2) = (sGProductN @f sn, sGProductN @g (sn %+ sGProductS @f))
in withSingI sm1 $ withSingI sm2 $ unionMap (m1, m2)
specProductN' (sn :: Sing n) m =
let (sm1, sm2) = (sGProductN @f sn, sGProductN @g (sn %+ sGProductS @f))
(x, y) =
withSingI sm1
$ withSingI sm2
$ ununionMap @(GProductN n f) @(GProductN (n + GProductS f) g) m
in specProductN' sn x :*: specProductN' (sn %+ sGProductS @f) y
class GenSum (f :: * -> *) where
type GSum f :: [Mapping Symbol Type]
genSum' :: f a -> NSum (GSum f)
specSum' :: NSum (GSum f) -> f a
instance GenSum f => GenSum (D1 _a f) where
type GSum (D1 _a f) = GSum f
genSum' = genSum' . unM1
specSum' = M1 . specSum'
instance GenProduct f => GenSum (C1 ('MetaCons n _a 'True) f) where
type GSum (C1 ( 'MetaCons n _a 'True) f) = '[Mappend "_" n ':-> NMap (GProduct f)]
genSum' (M1 x) = NSumThis (genProduct' x)
specSum' (NSumThis x) = M1 (specProduct' x)
specSum' (NSumThat _) = error "unreachable"
instance GenProductN f => GenSum (C1 ('MetaCons n _a 'False) f) where
type GSum (C1 ( 'MetaCons n _a 'False) f) = '[Mappend "_" n ':-> NMap (GProductN 1 f)]
genSum' (M1 x) = NSumThis (genProductN' (SNat @1) x)
specSum' (NSumThis x) = M1 (specProductN' (SNat @1) x)
specSum' (NSumThat _) = error "unreachable"
instance ( SingI (GSum f), SingI (GSum g)
, GenSum f, GenSum g) => GenSum (f :+: g) where
type GSum (f :+: g) = Union (GSum f) (GSum g)
genSum' (L1 x) = unionSum @(GSum f) @(GSum g) (Left (genSum' x))
genSum' (R1 x) = unionSum @(GSum f) @(GSum g) (Right (genSum' x))
specSum' m = case ununionSum @(GSum f) @(GSum g) m of
Left x -> L1 (specSum' x)
Right y -> R1 (specSum' y)
genProduct :: (Generic a, GenProduct (Rep a)) => a -> NMap (GProduct (Rep a))
genProduct = genProduct' . from
specProduct :: (Generic a, GenProduct (Rep a)) => NMap (GProduct (Rep a)) -> a
specProduct = to . specProduct'
genSum :: (Generic a, GenSum (Rep a)) => a -> NSum (GSum (Rep a))
genSum = genSum' . from
specSum :: (Generic a, GenSum (Rep a)) => NSum (GSum (Rep a)) -> a
specSum = to . specSum'