packages feed

domain-0.1: library/Domain/TH/InstanceDecs.hs

module Domain.TH.InstanceDecs
where

import Domain.Prelude
import DomainCore.Model
import qualified Domain.TH.InstanceDec as InstanceDec
import qualified Language.Haskell.TH as TH (Dec, Name)


hasField :: TypeDec -> [TH.Dec]
hasField (TypeDec typeName typeDef) =
  case typeDef of
    ProductTypeDef members ->
      zipWith zipper (enumFrom 0) members
      where
        numMembers =
          length members
        zipper offset (fieldName, fieldType) =
          InstanceDec.productHasField typeName fieldName fieldType numMembers offset
    SumTypeDef variants ->
      fmap mapper variants
      where
        mapper (variantName, memberTypes) =
          InstanceDec.sumHasField typeName variantName memberTypes

accessorIsLabel :: TypeDec -> [TH.Dec]
accessorIsLabel (TypeDec typeName typeDef) =
  case typeDef of
    ProductTypeDef members ->
      zipWith zipper (enumFrom 0) members
      where
        numMembers =
          length members
        zipper offset (fieldName, fieldType) =
          InstanceDec.productAccessorIsLabel typeName fieldName fieldType numMembers offset
    SumTypeDef variants ->
      variants &
      fmap (\ (variantName, memberTypes) ->
        InstanceDec.sumAccessorIsLabel typeName variantName memberTypes
        )

constructorIsLabel :: TypeDec -> [TH.Dec]
constructorIsLabel (TypeDec typeName typeDef) =
  case typeDef of
    ProductTypeDef members ->
      []
    SumTypeDef variants ->
      variants &
      fmap (\ (variantName, memberTypes) ->
        InstanceDec.curriedSumConstructorIsLabel typeName variantName memberTypes)

variantConstructorIsLabel :: Text -> (Text, [Type]) -> [TH.Dec]
variantConstructorIsLabel typeName (variantName, memberTypes) =
  let
    curried =
      InstanceDec.curriedSumConstructorIsLabel typeName variantName memberTypes
    uncurried =
      InstanceDec.uncurriedSumConstructorIsLabel typeName variantName memberTypes
    in case memberTypes of
      [] ->
        [curried]
      [_] ->
        [curried]
      _ ->
        [curried, uncurried]

mapperIsLabel :: TypeDec -> [TH.Dec]
mapperIsLabel (TypeDec typeName typeDef) =
  case typeDef of
    ProductTypeDef members ->
      zipWith zipper (enumFrom 0) members
      where
        numMembers =
          length members
        zipper offset (fieldName, fieldType) =
          InstanceDec.productMapperIsLabel typeName fieldName fieldType numMembers offset
    SumTypeDef variants ->
      do
        (variantName, memberTypes) <- variants
        if null memberTypes
          then empty
          else pure (InstanceDec.sumMapperIsLabel typeName variantName memberTypes)


-- * Deriving
-------------------------

byNonAliasName :: (Text -> TH.Dec) -> TypeDec -> [TH.Dec]
byNonAliasName cont (TypeDec a b) =
  [cont a]

byEnumName :: (Text -> TH.Dec) -> TypeDec -> [TH.Dec]
byEnumName cont (TypeDec name def) =
  case def of
    SumTypeDef variants | all (null . snd) variants ->
      [cont name]
    _ ->
      []

enum =
  byEnumName (InstanceDec.deriving_ ''Enum)

bounded =
  byEnumName (InstanceDec.deriving_ ''Bounded)

show =
  byNonAliasName (InstanceDec.deriving_ ''Show)

eq =
  byNonAliasName (InstanceDec.deriving_ ''Eq)

ord =
  byNonAliasName (InstanceDec.deriving_ ''Ord)

generic =
  byNonAliasName (InstanceDec.deriving_ ''Generic)

data_ =
  byNonAliasName (InstanceDec.deriving_ ''Data)

typeable =
  byNonAliasName (InstanceDec.deriving_ ''Typeable)

hashable =
  byNonAliasName (InstanceDec.empty ''Hashable)

lift =
  byNonAliasName (InstanceDec.deriving_ ''Lift)