mkKeyTypeDec :: MkPersistSettings -> EntityDef -> Q (Dec, [Dec])
mkKeyTypeDec mps t = do
    (instDecs, i) <-
      if mpsGeneric mps
        then if not useNewtype
               then do pfDec <- pfInstD
                       return (pfDec, [''Generic])
               else do gi <- genericNewtypeInstances
                       return (gi, [])
        else if not useNewtype
               then do pfDec <- pfInstD
                       return (pfDec, [''Show, ''Read, ''Eq, ''Ord, ''Generic])
                else do
                    let allInstances = [''Show, ''Read, ''Eq, ''Ord, ''PathPiece, ''PersistField, ''PersistFieldSql, ''ToJSON, ''FromJSON]
                    if customKeyType
                      then return ([], allInstances)
                      else do
                        bi <- backendKeyI
                        return (bi, allInstances)

    let kd = if useNewtype
               then NewtypeInstD [] k [recordType] dec i
               else DataInstD    [] k [recordType] [dec] i
    return (kd, instDecs)
  where
    keyConE = keyConExp t
    unKeyE = unKeyExp t
    dec = RecC (keyConName t) (keyFields mps t)
    k = ''Key
    recordType = genericDataType mps (entityHaskell t) backendT
    pfInstD = -- FIXME: generate a PersistMap instead of PersistList
      [d|instance PersistField (Key $(pure recordType)) where
            toPersistValue = PersistList . keyToValues
            fromPersistValue (PersistList l) = keyFromValues l
            fromPersistValue got = error $ "fromPersistValue: expected PersistList, got: " `mappend` show got
         instance PersistFieldSql (Key $(pure recordType)) where
            sqlType _ = SqlString
         instance ToJSON (Key $(pure recordType))
         instance FromJSON (Key $(pure recordType))
      |]

    keyStringL = StringL . keyString
    -- ghc 7.6 cannot parse the left arrow Ident $() <- lexP
    keyPattern = BindS (ConP 'Ident [LitP $ keyStringL t])

    backendKeyGenericI =
        [d| instance PersistStore $(pure backendT) =>
              ToBackendKey $(pure backendT) $(pure recordType) where
                toBackendKey   = $(return unKeyE)
                fromBackendKey = $(return keyConE)
        |]
    backendKeyI = let bdt = backendDataType mps in
        [d| instance ToBackendKey $(pure bdt) $(pure recordType) where
                toBackendKey   = $(return unKeyE)
                fromBackendKey = $(return keyConE)
        |]

    -- truly unfortunate that TH doesn't support standalone deriving
    -- https://ghc.haskell.org/trac/ghc/ticket/8100
    genericNewtypeInstances = do
      instances <- [|lexP|] >>= \lexPE -> [| step readPrec >>= return . ($(pure keyConE) )|] >>= \readE -> do
        alwaysInstances <-
          [d|instance Show (BackendKey $(pure backendT)) => Show (Key $(pure recordType)) where
              showsPrec i x = showParen (i > app_prec) $
                (showString $ $(pure $ LitE $ keyStringL t) `mappend` " ") .
                showsPrec i ($(return unKeyE) x)
                where app_prec = (10::Int)
             instance Read (BackendKey $(pure backendT)) => Read (Key $(pure recordType)) where
                readPrec = parens $ (prec app_prec $ $(pure $ DoE [keyPattern lexPE, NoBindS readE]))
                  where app_prec = (10::Int)
             instance Eq (BackendKey $(pure backendT)) => Eq (Key $(pure recordType)) where
                x == y =
                    ($(return unKeyE) x) ==
                    ($(return unKeyE) y)
                x /= y =
                    ($(return unKeyE) x) ==
                    ($(return unKeyE) y)
             instance Ord (BackendKey $(pure backendT)) => Ord (Key $(pure recordType)) where
                compare x y = compare
                    ($(return unKeyE) x)
                    ($(return unKeyE) y)
             instance PathPiece (BackendKey $(pure backendT)) => PathPiece (Key $(pure recordType)) where
                toPathPiece = toPathPiece . $(return unKeyE)
                fromPathPiece = fmap $(return keyConE) . fromPathPiece
             instance PersistField (BackendKey $(pure backendT)) => PersistField (Key $(pure recordType)) where
                toPersistValue = toPersistValue . $(return unKeyE)
                fromPersistValue = fmap $(return keyConE) . fromPersistValue
             instance PersistFieldSql (BackendKey $(pure backendT)) => PersistFieldSql (Key $(pure recordType)) where
                sqlType = sqlType . fmap $(return unKeyE)
             instance ToJSON (BackendKey $(pure backendT)) => ToJSON (Key $(pure recordType)) where
                toJSON = toJSON . $(return unKeyE)
             instance FromJSON (BackendKey $(pure backendT)) => FromJSON (Key $(pure recordType)) where
                parseJSON = fmap $(return keyConE) . parseJSON
              |]

        if customKeyType then return alwaysInstances
          else fmap (alwaysInstances `mappend`) backendKeyGenericI
      return instances

    useNewtype = pkNewtype mps t
    customKeyType = not (defaultIdType t) || not useNewtype || isJust (entityPrimary t)
mkKeyTypeDec :: MkPersistSettings -> EntityDef -> Q (Dec, [Dec])
mkKeyTypeDec mps t = do
    (instDecs, i) <-
      if mpsGeneric mps
        then if not useNewtype
               then do pfDec <- pfInstD
                       return (pfDec, [''Generic])
               else do gi <- genericNewtypeInstances
                       return (gi, [])
        else if not useNewtype
               then do pfDec <- pfInstD
                       return (pfDec, [''Show, ''Read, ''Eq, ''Ord, ''Generic])
                else do
                    let allInstances = [''Show, ''Read, ''Eq, ''Ord, ''PathPiece, ''PersistField, ''PersistFieldSql, ''ToJSON, ''FromJSON]
                    if customKeyType
                      then return ([], allInstances)
                      else do
                        bi <- backendKeyI
                        return (bi, allInstances)

    let kd = if useNewtype
               then NewtypeInstD [] k [recordType] dec i
               else DataInstD    [] k [recordType] [dec] i
    return (kd, instDecs)
  where
    keyConE = keyConExp t
    unKeyE = unKeyExp t
    dec = RecC (keyConName t) (keyFields mps t)
    k = ''Key
    recordType = genericDataType mps (entityHaskell t) backendT
    pfInstD = -- FIXME: generate a PersistMap instead of PersistList
      [d|instance PersistField (Key $(pure recordType)) where
            toPersistValue = PersistList . keyToValues
            fromPersistValue (PersistList l) = keyFromValues l
            fromPersistValue got = error $ "fromPersistValue: expected PersistList, got: " `mappend` show got
         instance PersistFieldSql (Key $(pure recordType)) where
            sqlType _ = SqlString
         instance ToJSON (Key $(pure recordType))
         instance FromJSON (Key $(pure recordType))
      |]

    keyStringL = StringL . keyString
    -- ghc 7.6 cannot parse the left arrow Ident $() <- lexP
    keyPattern = BindS (ConP 'Ident [LitP $ keyStringL t])

    backendKeyGenericI =
        [d| instance PersistStore $(pure backendT) =>
              ToBackendKey $(pure backendT) $(pure recordType) where
                toBackendKey   = $(return unKeyE)
                fromBackendKey = $(return keyConE)
        |]
    backendKeyI = let bdt = backendDataType mps in
        [d| instance ToBackendKey $(pure bdt) $(pure recordType) where
                toBackendKey   = $(return unKeyE)
                fromBackendKey = $(return keyConE)
        |]

    -- truly unfortunate that TH doesn't support standalone deriving
    -- https://ghc.haskell.org/trac/ghc/ticket/8100
    genericNewtypeInstances = do
      instances <- [|lexP|] >>= \lexPE -> [| step readPrec >>= return . ($(pure keyConE) )|] >>= \readE -> do
        alwaysInstances <-
          [d|instance Show (BackendKey $(pure backendT)) => Show (Key $(pure recordType)) where
              showsPrec i x = showParen (i > app_prec) $
                (showString $ $(pure $ LitE $ keyStringL t) `mappend` " ") .
                showsPrec i ($(return unKeyE) x)
                where app_prec = (10::Int)
             instance Read (BackendKey $(pure backendT)) => Read (Key $(pure recordType)) where
                readPrec = parens $ (prec app_prec $ $(pure $ DoE [keyPattern lexPE, NoBindS readE]))
                  where app_prec = (10::Int)
             instance Eq (BackendKey $(pure backendT)) => Eq (Key $(pure recordType)) where
                x == y =
                    ($(return unKeyE) x) ==
                    ($(return unKeyE) y)
                x /= y =
                    ($(return unKeyE) x) ==
                    ($(return unKeyE) y)
             instance Ord (BackendKey $(pure backendT)) => Ord (Key $(pure recordType)) where
                compare x y = compare
                    ($(return unKeyE) x)
                    ($(return unKeyE) y)
             instance PathPiece (BackendKey $(pure backendT)) => PathPiece (Key $(pure recordType)) where
                toPathPiece = toPathPiece . $(return unKeyE)
                fromPathPiece = fmap $(return keyConE) . fromPathPiece
             instance PersistField (BackendKey $(pure backendT)) => PersistField (Key $(pure recordType)) where
                toPersistValue = toPersistValue . $(return unKeyE)
                fromPersistValue = fmap $(return keyConE) . fromPersistValue
             instance PersistFieldSql (BackendKey $(pure backendT)) => PersistFieldSql (Key $(pure recordType)) where
                sqlType = sqlType . fmap $(return unKeyE)
             instance ToJSON (BackendKey $(pure backendT)) => ToJSON (Key $(pure recordType)) where
                toJSON = toJSON . $(return unKeyE)
             instance FromJSON (BackendKey $(pure backendT)) => FromJSON (Key $(pure recordType)) where
                parseJSON = fmap $(return keyConE) . parseJSON
              |]

        if customKeyType then return alwaysInstances
          else fmap (alwaysInstances `mappend`) backendKeyGenericI
      return instances

    useNewtype = pkNewtype mps t
    customKeyType = not (defaultIdType t) || not useNewtype || isJust (entityPrimary t)
mkKeyTypeDec mps t = do
    (instDecs, i) <-
      if mpsGeneric mps
        then if not useNewtype
               then do pfDec <- pfInstD
                       return (pfDec, [''Generic])
               else do gi <- genericNewtypeInstances
                       return (gi, [])
        else if not useNewtype
               then do pfDec <- pfInstD
                       return (pfDec, [''Show, ''Read, ''Eq, ''Ord, ''Generic])
                else do
                    let allInstances = [''Show, ''Read, ''Eq, ''Ord, ''PathPiece, ''PersistField, ''PersistFieldSql, ''ToJSON, ''FromJSON]
                    if customKeyType
                      then return ([], allInstances)
                      else do
                        bi <- backendKeyI
                        return (bi, allInstances)

    let kd = if useNewtype
               then NewtypeInstD [] k [recordType] dec i
               else DataInstD    [] k [recordType] [dec] i
    return (kd, instDecs)
  where
    keyConE = keyConExp t
    unKeyE = unKeyExp t
    dec = RecC (keyConName t) (keyFields mps t)
    k = ''Key
    recordType = genericDataType mps (entityHaskell t) backendT
    pfInstD = -- FIXME: generate a PersistMap instead of PersistList
      [d|instance PersistField (Key $(pure recordType)) where
            toPersistValue = PersistList . keyToValues
            fromPersistValue (PersistList l) = keyFromValues l
            fromPersistValue got = error $ "fromPersistValue: expected PersistList, got: " `mappend` show got
         instance PersistFieldSql (Key $(pure recordType)) where
            sqlType _ = SqlString
         instance ToJSON (Key $(pure recordType))
         instance FromJSON (Key $(pure recordType))
      |]

    keyStringL = StringL . keyString
    -- ghc 7.6 cannot parse the left arrow Ident $() <- lexP
    keyPattern = BindS (ConP 'Ident [LitP $ keyStringL t])

    backendKeyGenericI =
        [d| instance PersistStore $(pure backendT) =>
              ToBackendKey $(pure backendT) $(pure recordType) where
                toBackendKey   = $(return unKeyE)
                fromBackendKey = $(return keyConE)
        |]
    backendKeyI = let bdt = backendDataType mps in
        [d| instance ToBackendKey $(pure bdt) $(pure recordType) where
                toBackendKey   = $(return unKeyE)
                fromBackendKey = $(return keyConE)
        |]

    -- truly unfortunate that TH doesn't support standalone deriving
    -- https://ghc.haskell.org/trac/ghc/ticket/8100
    genericNewtypeInstances = do
      instances <- [|lexP|] >>= \lexPE -> [| step readPrec >>= return . ($(pure keyConE) )|] >>= \readE -> do
        alwaysInstances <-
          [d|instance Show (BackendKey $(pure backendT)) => Show (Key $(pure recordType)) where
              showsPrec i x = showParen (i > app_prec) $
                (showString $ $(pure $ LitE $ keyStringL t) `mappend` " ") .
                showsPrec i ($(return unKeyE) x)
                where app_prec = (10::Int)
             instance Read (BackendKey $(pure backendT)) => Read (Key $(pure recordType)) where
                readPrec = parens $ (prec app_prec $ $(pure $ DoE [keyPattern lexPE, NoBindS readE]))
                  where app_prec = (10::Int)
             instance Eq (BackendKey $(pure backendT)) => Eq (Key $(pure recordType)) where
                x == y =
                    ($(return unKeyE) x) ==
                    ($(return unKeyE) y)
                x /= y =
                    ($(return unKeyE) x) ==
                    ($(return unKeyE) y)
             instance Ord (BackendKey $(pure backendT)) => Ord (Key $(pure recordType)) where
                compare x y = compare
                    ($(return unKeyE) x)
                    ($(return unKeyE) y)
             instance PathPiece (BackendKey $(pure backendT)) => PathPiece (Key $(pure recordType)) where
                toPathPiece = toPathPiece . $(return unKeyE)
                fromPathPiece = fmap $(return keyConE) . fromPathPiece
             instance PersistField (BackendKey $(pure backendT)) => PersistField (Key $(pure recordType)) where
                toPersistValue = toPersistValue . $(return unKeyE)
                fromPersistValue = fmap $(return keyConE) . fromPersistValue
             instance PersistFieldSql (BackendKey $(pure backendT)) => PersistFieldSql (Key $(pure recordType)) where
                sqlType = sqlType . fmap $(return unKeyE)
             instance ToJSON (BackendKey $(pure backendT)) => ToJSON (Key $(pure recordType)) where
                toJSON = toJSON . $(return unKeyE)
             instance FromJSON (BackendKey $(pure backendT)) => FromJSON (Key $(pure recordType)) where
                parseJSON = fmap $(return keyConE) . parseJSON
              |]

        if customKeyType then return alwaysInstances
          else fmap (alwaysInstances `mappend`) backendKeyGenericI
      return instances

    useNewtype = pkNewtype mps t
    customKeyType = not (defaultIdType t) || not useNewtype || isJust (entityPrimary t)