{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE IncoherentInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Generics.Eot.Datatype where
import Data.Maybe
import Data.Proxy
import qualified GHC.Generics as GHC
import GHC.Generics hiding (Datatype(..), Constructor(..))
data Datatype
= Datatype {
datatypeName :: String,
constructors :: [Constructor]
}
deriving (Show, Eq)
data Constructor
= Constructor {
constructorName :: String,
fields :: Fields
}
deriving (Show, Eq)
data Fields
= Selectors [String]
| NoSelectors Int
| NoFields
deriving (Show, Eq)
class GenericDatatype (a :: * -> *) where
datatypeC :: Proxy a -> Datatype
instance (GHC.Datatype c, GenericConstructors f) =>
GenericDatatype (D1 c f) where
datatypeC Proxy = Datatype n constructors
where
n = GHC.datatypeName (undefined :: D1 c f x)
constructors = getConstructors (Proxy :: Proxy f)
class GenericConstructors (a :: * -> *) where
getConstructors :: Proxy a -> [Constructor]
instance (GenericConstructors a, GenericConstructors b) =>
GenericConstructors (a :+: b) where
getConstructors Proxy = getConstructors a ++ getConstructors b
where
a :: Proxy a = Proxy
b :: Proxy b = Proxy
instance (GHC.Constructor c, GenericFields f) =>
GenericConstructors (C1 c f) where
getConstructors Proxy = [Constructor n (getFields f)]
where
n = GHC.conName (undefined :: (C1 c f x))
f :: Proxy f = Proxy
instance GenericConstructors V1 where
getConstructors Proxy = []
getFields :: GenericFields a => Proxy a -> Fields
getFields proxy = case getFieldsC proxy of
[] -> NoFields
l@(Nothing : _) -> NoSelectors (length l)
l@(Just _ : _) -> Selectors (catMaybes l)
class GenericFields (a :: * -> *) where
getFieldsC :: Proxy a -> [Maybe String]
instance (GenericFields a, GenericFields b) =>
GenericFields (a :*: b) where
getFieldsC Proxy = getFieldsC a ++ getFieldsC b
where
a :: Proxy a = Proxy
b :: Proxy b = Proxy
instance Selector c => GenericFields (S1 c (Rec0 f)) where
getFieldsC proxy = [getField proxy]
getField :: forall c f . Selector c =>
Proxy (S1 c (Rec0 f)) -> Maybe String
getField Proxy = case selName (undefined :: S1 c (Rec0 f) x) of
"" -> Nothing
s -> Just s
instance GenericFields U1 where
getFieldsC Proxy = []