{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeSynonymInstances #-} module Language.PureScript.Bridge.SumType where import Generics.Deriving import Data.Text (Text) import qualified Data.Text as T import Data.Proxy import Data.Typeable import Language.PureScript.Bridge.TypeInfo -- | Generic representation of your Haskell types, the contained (leaf) types can be modified to match -- compatible PureScript types, by using 'TypeBridge' functions like 'defaultBridge' with 'writePSTypes'. data SumType = SumType TypeInfo [DataConstructor] deriving Show -- | Create a representation of your sum (and product) types, -- for doing type translations and writing it out to your PureScript modules. -- In order to get the type information we use a dummy variable of type Proxy (YourType). toSumType :: forall t. (Generic t, Typeable t, GDataConstructor (Rep t)) => Proxy t -> SumType toSumType p = SumType (mkTypeInfo p) constructors where constructors = gToConstructors (from (undefined :: t)) data DataConstructor = DataConstructor { sigConstructor :: !Text , sigValues :: !(Either [TypeInfo] [RecordEntry]) } deriving Show data RecordEntry = RecordEntry { recLabel :: !Text , recValue :: !TypeInfo } deriving Show class GDataConstructor f where gToConstructors :: f a -> [DataConstructor] class GRecordEntry f where gToRecordEntries :: f a -> [RecordEntry] instance (Datatype a, GDataConstructor c) => GDataConstructor (D1 a c) where gToConstructors (M1 c) = gToConstructors c instance (GDataConstructor a, GDataConstructor b) => GDataConstructor (a :+: b) where gToConstructors (_ :: (a :+: b) f) = gToConstructors (undefined :: a f) ++ gToConstructors (undefined :: b f) instance (Constructor a, GRecordEntry b) => GDataConstructor (C1 a b) where gToConstructors c@(M1 r) = [ DataConstructor { sigConstructor = constructor , sigValues = values } ] where constructor = T.pack $ conName c values = if conIsRecord c then Right $ gToRecordEntries r else Left $ map recValue $ gToRecordEntries r instance (GRecordEntry a, GRecordEntry b) => GRecordEntry (a :*: b) where gToRecordEntries (_ :: (a :*: b) f) = gToRecordEntries (undefined :: a f) ++ gToRecordEntries (undefined :: b f) instance GRecordEntry U1 where gToRecordEntries _ = [] instance (Selector a, Typeable t) => GRecordEntry (S1 a (K1 R t)) where gToRecordEntries e = [ RecordEntry { recLabel = T.pack (selName e) , recValue = mkTypeInfo (Proxy :: Proxy t) } ] getUsedTypes :: SumType -> [TypeInfo] getUsedTypes (SumType _ cs) = foldr constructorToType [] cs constructorToType :: DataConstructor -> [TypeInfo] -> [TypeInfo] constructorToType (DataConstructor _ (Left myTs)) ts = concatMap flattenTypeInfo myTs ++ ts constructorToType (DataConstructor _ (Right rs)) ts = concatMap (flattenTypeInfo . recValue) rs ++ ts