packages feed

g2-0.2.0.0: src/G2/Language/Naming.hs

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

module G2.Language.Naming
    ( nameOcc
    , nameModule
    , nameUnique
    , nameTuple
    , nameLoc
    , NameGen
    , Named (..)
    , namesList
    , doRename
    , doRenames
    , renameAll
    , nameToStr
    , nameToBuilder
    , strToName
    , maybe_StrToName
    , builderToName

    , mkNameGen
    , varIds
    , varNames
    , exprNames
    , typeNames

    , renameExprs
    , renamesExprs
    , renameExpr
    , renameVars

    , freshSeededString
    , freshSeededStrings

    , freshName
    , freshNames
    , freshSeededName
    , freshSeededNames

    , freshId
    , freshSeededId
    , freshIds
    , freshVar

    , mapNG
    ) where

import qualified G2.Data.UFMap as UF
import G2.Language.AST
import G2.Language.KnownValues
import G2.Language.Syntax
import G2.Language.TypeEnv

import Data.Data (Data, Typeable)
import Data.Foldable
import Data.Hashable
import qualified Data.HashMap.Lazy as HM
import qualified Data.HashSet as HS
import Data.List
import Data.List.Utils
import qualified Data.Map as M
import Data.Monoid ((<>))
import qualified Data.Sequence as S
import qualified Data.Text as T
import Data.Tuple
import qualified Text.Builder as TB

-- | Extract the "occurence" from a `Name`.
--
-- >>> nameOcc (Name "x" (Just "mod") 100 Nothing)
-- "x"
nameOcc :: Name -> T.Text
nameOcc (Name occ _ _ _) = occ

-- | Extract the module from a `Name`.
--
-- >>> nameModule (Name "x" (Just "mod") 100 Nothing)
-- Just "mod"
nameModule :: Name -> Maybe T.Text
nameModule (Name _ mb _ _) = mb

-- | Extract the "unique" from a `Name`.
--
-- >>> nameUnique (Name "x" (Just "mod") 100 Nothing)
-- 100
nameUnique :: Name -> Int
nameUnique (Name _ _ i _) = i

-- | Get a tuple with the "occurence" and module of a `Name`.
--
-- >>> nameTuple (Name "x" (Just "mod") 100 Nothing)
-- ("x", Just "mod")
nameTuple :: Name -> (T.Text, Maybe T.Text)
nameTuple n = (nameOcc n, nameModule n)

-- | Get the location of a `Name`.
nameLoc :: Name -> Maybe Span
nameLoc (Name _ _ _ s) = s

-- | Allows the creation of fresh `Name`s.
newtype NameGen = NameGen (HM.HashMap (T.Text, Maybe T.Text) Int)
                deriving (Show, Eq, Read, Typeable, Data)

-- nameToStr relies on NameCleaner eliminating all '_', to preserve uniqueness
-- | Converts a `Name` to a string, which is useful to interact with solvers.
nameToStr :: Name -> String
nameToStr (Name n (Just m) i _) = T.unpack n ++ "_m_" ++ T.unpack m ++ "_" ++ show i
nameToStr (Name n Nothing i _) = T.unpack n ++ "_n__" ++ show i

-- | Similar to `nameToStr`, but converts a `Name` to a `TB.Builder`.
nameToBuilder :: Name -> TB.Builder
nameToBuilder (Name n (Just m) i _) = mconcat [TB.text n, TB.text "_m_", TB.text m, TB.text "_", TB.string $ show i]
nameToBuilder (Name n Nothing i _) = mconcat [TB.text n, TB.text "_n__", TB.string $ show i]

-- | Converts a `String` generated by `nameToStr` to a `Name`.
-- Loses location information
strToName :: String -> Name
strToName str =
    case maybe_StrToName str of
        Just n -> n
        Nothing -> error "strToName: Bad conversion"

maybe_StrToName :: String -> Maybe Name
maybe_StrToName str
    | (n, _:q:_:mi) <- breakList (\s -> isPrefixOf "_m_" s || isPrefixOf "_n_" s) str
    , (m, _:i) <- break ((==) '_') mi =
    let
        m' = if q == 'm' then Just m else Nothing
    in
    Just $ Name (T.pack n) (fmap T.pack m') (read i :: Int) Nothing
    | otherwise = Nothing

-- | Converts a `TB.Builder` generated by `nameToBuilder` to a `Name`.
-- Loses location information
builderToName :: TB.Builder -> Name
builderToName txt = strToName $ show txt

-- | Initializes a `NameGen`.  The returned `NameGen` is guarenteed to not give any `Name`
-- in the given `Named` type.
mkNameGen :: Named n => n -> NameGen
mkNameGen nmd =
    let
        allNames = toList $ names nmd
    in
    NameGen (HM.fromListWith max $ map (\(Name n m i _) -> ((n, m), i + 1)) allNames)

-- | Returns all @Var@ Ids in an ASTContainer
varIds :: (ASTContainer m Expr) => m -> [Id]
varIds = evalASTs varIds'

varIds' :: Expr -> [Id]
varIds' (Var i) = [i]
varIds' _ = []

varNames :: (ASTContainer m Expr) => m -> [Name]
varNames = map idName . varIds

-- Returns every `Name` that appears in an `Expr`, but ignores those only in `Type`s.
exprNames :: (ASTContainer m Expr) => m -> [Name]
exprNames = evalASTs exprTopNames

exprTopNames :: Expr -> [Name]
exprTopNames (Var var) = [idName var]
exprTopNames (Data dc) = dataConName dc
exprTopNames (Lam _ b _) = [idName b]
exprTopNames (Let kvs _) = map (idName . fst) kvs
exprTopNames (Case _ cvar _ as) = idName cvar :
                                concatMap (\(Alt am _) -> altMatchNames am)
                                          as
exprTopNames (Assume (Just is) _ _) = [funcName is]
exprTopNames (Assert (Just is) _ _) = [funcName is]
exprTopNames _ = []

altMatchNames :: AltMatch -> [Name]
altMatchNames (DataAlt dc i) = dataConName dc ++ (map idName i)
altMatchNames _ = []

dataConName :: DataCon -> [Name]
dataConName (DataCon n _) = [n]

typeNames :: (ASTContainer m Type) => m -> [Name]
typeNames = evalASTs typeTopNames

typeTopNames :: Type -> [Name]
typeTopNames (TyVar i) = [idName i]
typeTopNames (TyCon n _) = [n]
typeTopNames (TyForAll v _) = [idName v]
typeTopNames _ = []

doRename :: Named a => Name -> NameGen -> a -> (a, NameGen)
doRename n ngen x = (rename n n' x, ngen')
  where (n', ngen') = freshSeededName n ngen

doRenames :: Named a => [Name] -> NameGen -> a -> (a, NameGen)
doRenames ns ng e =
    let
        (ns', ng') = freshSeededNames ns ng
        hm = HM.fromList $ zip ns ns'
    in
    (renames hm e, ng')

renameAll :: (Named a) => a -> NameGen -> (a, NameGen)
renameAll x ng =
    let
        old = nub . toList $ names x
    in
    doRenames old ng x

namesList :: Named a => a -> [Name]
namesList = toList . names

-- | Types that contain `Name`@s@
class Named a where
    names :: a -> S.Seq Name
    rename :: Name -> Name -> a -> a
    renames :: HM.HashMap Name Name -> a -> a

    renames hm e = HM.foldrWithKey (\k v -> rename k v) e hm

instance Named Name where
    {-# INLINE names #-}
    names n = S.singleton n
    {-# INLINE rename #-}
    rename old (Name nn nm ni _) n@(Name _ _ _ l) = if old == n then Name nn nm ni l else n
    {-# INLINE renames #-}
    renames hm n@(Name _ _ _ l) =
        case HM.lookupDefault n n hm of
            Name n' m' i _ -> Name n' m' i l

instance Named Id where
    {-# INLINE names #-}
    names (Id n t) = n S.<| names t
    {-# INLINE rename #-}
    rename old new (Id n t) = Id (rename old new n) (rename old new t)
    {-# INLINE renames #-}
    renames hm (Id n t) = Id (renames hm n) (renames hm t)

instance Named Expr where
    names = eval go
        where
            go :: Expr -> S.Seq Name
            go (Var i) = names i
            go (Prim _ t) = names t
            go (Data d) = names d
            go (Lam _ i _) = names i
            go (Let b _) = foldr (<>) S.empty $ map (names . fst) b
            go (Case _ i t a) = names i <> names t <> (foldr (<>) S.empty $ map (names . altMatch) a)
            go (Type t) = names t
            go (Cast _ c) = names c
            go (Coercion c) = names c
            go (Tick t _) = names t
            go (SymGen _ t) = names t
            go (Assume is _ _) = names is
            go (Assert is _ _) = names is
            go _ = S.empty

    rename old new = modify go
      where
        go :: Expr -> Expr
        go (Var i) = Var (rename old new i)
        go (Data d) = Data (rename old new d)
        go (Lam u i e) = Lam u (rename old new i) e
        go (Let b e) =
            let b' = map (\(n, e') -> (rename old new n, e')) b
            in Let b' e
        go (Case e i t a) =
            Case e (rename old new i) (rename old new t) (map goAlt a)
        go (Type t) = Type (rename old new t)
        go (Cast e c) = Cast e (rename old new c)
        go (Coercion c) = Coercion (rename old new c)
        go (Tick t e) = Tick (rename old new t) e
        go (SymGen sl t) = SymGen sl (rename old new t)
        go (Assume is e e') = Assume (rename old new is) e e'
        go (Assert is e e') = Assert (rename old new is) e e'
        go e = e

        goAlt :: Alt -> Alt
        goAlt (Alt am e) = Alt (rename old new am) e

    renames hm = modify go
        where
            go :: Expr -> Expr
            go (Var i) = Var (renames hm i)
            go (Data d) = Data (renames hm d)
            go (Lam u i e) = Lam u (renames hm i) e
            go (Let b e) = 
                let b' = map (\(n, e') -> (renames hm n, e')) b
                in Let b' e
            go (Case e i t a) = Case e (renames hm i) (renames hm t) (map goAlt a)
            go (Type t) = Type (renames hm t)
            go (Cast e c) = Cast e (renames hm c)
            go (Coercion c) = Coercion (renames hm c)
            go (Tick t e) = Tick (renames hm t) e
            go (SymGen sl t) = SymGen sl (renames hm t)
            go (Assume is e e') = Assume (renames hm is) e e'
            go (Assert is e e') = Assert (renames hm is) e e'
            go e = e

            goAlt :: Alt -> Alt
            goAlt (Alt am e) = Alt (renames hm am) e

renameExprs :: ASTContainer m Expr => [(Name, Name)] -> m -> m
renameExprs n = renamesExprs (HM.fromList n)

-- | Rename only the names in an `Expr` that are the `Name` of an `Id`/`Let`/`Data`/`Case` Binding.
-- Does not change Types.
renameExpr :: ASTContainer m Expr => Name -> Name -> m -> m
renameExpr old new = modifyASTs (renameExpr' old new)

renameExpr' :: Name -> Name -> Expr -> Expr
renameExpr' old new (Var i) = Var (renameExprId old new i)
renameExpr' old new (Data d) = Data (renameExprDataCon old new d)
renameExpr' old new (Lam u i e) = Lam u (renameExprId old new i) e
renameExpr' old new (Let b e) = Let (map (\(b', e') -> (renameExprId old new b', e')) b) e
renameExpr' old new (Case e i t a) = Case e (renameExprId old new i) t $ map (renameExprAlt old new) a
renameExpr' old new (Assume is e e') = Assume (fmap (rename old new) is) e e'
renameExpr' old new (Assert is e e') = Assert (fmap (rename old new) is) e e'
renameExpr' _ _ e = e

-- | Renames only the @Vars@ in an `Expr`.
renameVars :: ASTContainer m Expr => Name -> Name -> m -> m
renameVars old new = modifyASTs (renameVars' old new)

renameVars' :: Name -> Name -> Expr -> Expr
renameVars' old new (Var i) = Var (renameExprId old new i)
renameVars' old new (Lam u i e) = Lam u (renameExprId old new i) e
renameVars' old new (Let b e) = Let (map (\(b', e') -> (renameExprId old new b', e')) b) e
renameVars' old new (Case e i t a) = Case e (renameExprId old new i) t $ map (renameExprAltIds old new) a
renameVars' old new (Assert is e e') = Assert (fmap (rename old new) is) e e'
renameVars' _ _ e = e


renameExprId :: Name -> Name -> Id -> Id
renameExprId old new (Id n t) = Id (rename old new n) t

renameExprDataCon :: Name -> Name -> DataCon -> DataCon
renameExprDataCon old new (DataCon n t) = DataCon (rename old new n) t

renameExprAlt :: Name -> Name -> Alt -> Alt
renameExprAlt old new (Alt (DataAlt dc is) e) =
    let
        dc' = renameExprDataCon old new dc
        is' = map (renameExprId old new) is
    in
    Alt (DataAlt dc' is') e
renameExprAlt _ _ a = a

renameExprAltIds :: Name -> Name -> Alt -> Alt
renameExprAltIds old new (Alt (DataAlt dc is) e) =
    let
        is' = map (renameExprId old new) is
    in
    Alt (DataAlt dc is') e
renameExprAltIds _ _ a = a

renamesExprs :: ASTContainer m Expr => HM.HashMap Name Name -> m -> m
renamesExprs hm = modifyASTs (renamesExprs' hm)

renamesExprs' :: HM.HashMap Name Name -> Expr -> Expr
renamesExprs' hm (Var i) = Var (renamesExprId hm i)
renamesExprs' hm (Data d) = Data (renamesExprDataCon hm d)
renamesExprs' hm (Lam u i e) = Lam u (renamesExprId hm i) e
renamesExprs' hm (Let b e) = Let (map (\(b', e') -> (renamesExprId hm b', e')) b) e
renamesExprs' hm (Case e i t a) = Case e (renamesExprId hm i) t $ map (renamesExprAlt hm) a
renamesExprs' hm (Assume is e e') = Assume (fmap (renames hm) is) e e'
renamesExprs' hm (Assert is e e') = Assert (fmap (renames hm) is) e e'
renamesExprs' _ e = e

renamesExprId :: HM.HashMap Name Name -> Id -> Id
renamesExprId hm (Id n t) = Id (renames hm n) t

renamesExprDataCon :: HM.HashMap Name Name -> DataCon -> DataCon
renamesExprDataCon hm (DataCon n t) = DataCon (renames hm n) t

renamesExprAlt :: HM.HashMap Name Name -> Alt -> Alt
renamesExprAlt hm (Alt (DataAlt dc is) e) =
    let
        dc' = renamesExprDataCon hm dc
        is' = map (renamesExprId hm) is
    in
    Alt (DataAlt dc' is') e
renamesExprAlt _ a = a

instance Named Type where
    names = eval go
        where
            go (TyVar i) = idNamesInType i
            go (TyCon n _) = S.singleton n
            go (TyForAll b _) = idNamesInType b
            go _ = S.empty

    rename old new = modify go
      where
        go :: Type -> Type
        go (TyVar i) = TyVar (renameIdInType old new i)
        go (TyCon n ts) = TyCon (rename old new n) ts
        go (TyForAll tb t) = TyForAll (renameIdInType old new tb) t
        go t = t

    renames hm = modify go
      where
        go :: Type -> Type
        go (TyVar i) = TyVar (renamesIdInType hm i)
        go (TyCon n ts) = TyCon (renames hm n) ts
        go (TyForAll tb t) = TyForAll (renamesIdInType hm tb) t
        go t = t

-- We don't want both modify and go to recurse on the Type's in Ids
-- so we introduce functions to collect or rename only the Names directly in those types
idNamesInType :: Id -> S.Seq Name
idNamesInType (Id n _) = S.singleton n

renameIdInType :: Name -> Name -> Id -> Id
renameIdInType old new (Id n t) = Id (rename old new n) t

renamesIdInType :: HM.HashMap Name Name -> Id -> Id
renamesIdInType hm (Id n t) = Id (renames hm n) t

instance Named Alt where
    {-# INLINE names #-}
    names (Alt am e) = names am <> names e

    {-# INLINE rename #-}
    rename old new (Alt am e) = Alt (rename old new am) (rename old new e)

    {-# INLINE renames #-}
    renames hm (Alt am e) = Alt (renames hm am) (renames hm e)

instance Named DataCon where
    {-# INLINE names #-}
    names (DataCon n t) = n S.<| names t

    {-# INLINE rename #-}
    rename old new (DataCon n t) =
        DataCon (rename old new n) (rename old new t)

    {-# INLINE renames #-}
    renames hm (DataCon n t) =
        DataCon (renames hm n) (renames hm t)

instance Named AltMatch where
    {-# INLINE names #-}
    names (DataAlt dc i) = names dc <> names i
    names _ = S.empty

    {-# INLINE rename #-}
    rename old new (DataAlt dc i) =
        DataAlt (rename old new dc) (rename old new i)
    rename _ _ am = am

    {-# INLINE renames #-}
    renames hm (DataAlt dc i) =
        DataAlt (renames hm dc) (renames hm i)
    renames _ am = am

instance Named Coercion where
    names (t1 :~ t2) = names t1 <> names t2
    rename old new (t1 :~ t2) = rename old new t1 :~ rename old new t2
    renames hm (t1 :~ t2) = renames hm t1 :~ renames hm t2

instance Named Tickish where
    names (Breakpoint _) = S.empty
    names (HpcTick _ _) = S.empty
    names (NamedLoc n) = S.singleton n

    rename _ _ bp@(Breakpoint _) = bp
    rename _ _ hpc@(HpcTick _ _) = hpc
    rename old new (NamedLoc n) = NamedLoc $ rename old new n

    renames _ bp@(Breakpoint _) = bp
    renames _ hpc@(HpcTick _ _) = hpc
    renames hm (NamedLoc n) = NamedLoc $ renames hm n

instance Named RewriteRule where
    names (RewriteRule { ru_head = h
                       , ru_rough = rs
                       , ru_bndrs = b
                       , ru_args = as
                       , ru_rhs = rhs}) =
        h S.<| names rs <> names b <> names as <> names rhs

    rename old new (RewriteRule { ru_name = n
                                , ru_module = mdl
                                , ru_head = h
                                , ru_rough = rs
                                , ru_bndrs = b
                                , ru_args = as
                                , ru_rhs = rhs}) =
        RewriteRule { ru_name = n
                    , ru_module = mdl
                    , ru_head = rename old new h
                    , ru_rough = rename old new rs
                    , ru_bndrs = rename old new b
                    , ru_args = rename old new as
                    , ru_rhs = rename old new rhs}

    renames hm (RewriteRule { ru_name = n
                            , ru_module = mdl
                            , ru_head = h
                            , ru_rough = rs
                            , ru_bndrs = b
                            , ru_args = as
                            , ru_rhs = rhs}) =
        RewriteRule { ru_name = n
                    , ru_module = mdl
                    , ru_head = renames hm h
                    , ru_rough = renames hm rs
                    , ru_bndrs = renames hm b
                    , ru_args = renames hm as
                    , ru_rhs = renames hm rhs}

instance Named FuncCall where
    names (FuncCall {funcName = n, arguments = as, returns = r}) = n S.<| names as <> names r
    rename old new (FuncCall {funcName = n, arguments = as, returns = r}) = 
        FuncCall {funcName = rename old new n, arguments = rename old new as, returns = rename old new r}
    renames hm (FuncCall {funcName = n, arguments = as, returns = r} ) =
        FuncCall {funcName = renames hm n, arguments = renames hm as, returns = renames hm r}


instance Named AlgDataTy where
    names (DataTyCon ns dc) = names ns <> names dc
    names (NewTyCon ns dc rt) = names ns <> names dc <> names rt
    names (TypeSynonym is st) = names is <> names st

    rename old new (DataTyCon n dc) = DataTyCon (rename old new n) (rename old new dc)
    rename old new (NewTyCon n dc rt) = NewTyCon (rename old new n) (rename old new dc) (rename old new rt)
    rename old new (TypeSynonym is st) = (TypeSynonym (rename old new is) (rename old new st))

    renames hm (DataTyCon n dc) = DataTyCon (renames hm n) (renames hm dc)
    renames hm (NewTyCon n dc rt) = NewTyCon (renames hm n) (renames hm dc) (renames hm rt)
    renames hm (TypeSynonym is st) = TypeSynonym (renames hm is) (renames hm st)

instance Named KnownValues where
    names (KnownValues {
              dcInt = dI
            , dcFloat = dF
            , dcDouble = dD
            , dcInteger = dI2
            , dcChar = dcCh

            , tyInt = tI
            , tyFloat = tF
            , tyDouble = tD
            , tyInteger = tI2
            , tyChar = tCh

            , tyBool = tB
            , dcTrue = dcT
            , dcFalse = dcF

            , tyRational = tR

            , tyList = tList
            , dcCons = tCons
            , dcEmpty = tEmp

            , tyMaybe = tMaybe
            , dcJust = dJust
            , dcNothing = dNothing

            , tyUnit = tUnit
            , dcUnit = dUnit

            , eqTC = eqT
            , numTC = numT
            , ordTC = ordT
            , integralTC = integralT
            , realTC = realT
            , fractionalTC = fractionalT

            , integralExtactReal = integralEReal
            , realExtractNum = realENum
            , realExtractOrd = realEOrd
            , ordExtractEq = ordEEq

            , eqFunc = eqF
            , neqFunc = neqF

            , plusFunc = plF
            , minusFunc = minusF
            , timesFunc = tmsF
            , divFunc = divF
            , negateFunc = negF
            , modFunc = modF

            , fromIntegerFunc = fromIntegerF
            , toIntegerFunc = toIntegerF

            , toRatioFunc = toRatioF
            , fromRationalFunc = fromRationalF

            , geFunc = geF
            , gtFunc = gtF
            , ltFunc = ltF
            , leFunc = leF

            , impliesFunc = impF
            , iffFunc = iffF

            , andFunc = andF
            , orFunc = orF
            , notFunc = notF

            , errorFunc = errF
            , errorEmptyListFunc = errEmpListF
            , errorWithoutStackTraceFunc = errWOST
            , patErrorFunc = patE
            }) =
            S.fromList
                [dI, dF, dD, dI2, dcCh, tI, tI2, tF, tD, tCh, tB, dcT, dcF, tR
                , tList, tCons, tEmp
                , tMaybe, dJust, dNothing
                , tUnit, dUnit
                , eqT, numT, ordT, integralT, realT, fractionalT
                , integralEReal, realENum, realEOrd, ordEEq
                , eqF, neqF, plF, minusF, tmsF, divF, negF, modF
                , fromIntegerF, toIntegerF
                , toRatioF, fromRationalF
                , geF, gtF, ltF, leF
                , impF, iffF
                , andF, orF, notF
                , errF, errEmpListF, errWOST, patE]

    rename old new (KnownValues {
                     dcInt = dI
                   , dcFloat = dF
                   , dcDouble = dD
                   , dcInteger = dI2
                   , dcChar = dcCh

                   , tyInt = tI
                   , tyFloat = tF
                   , tyDouble = tD
                   , tyInteger = tI2
                   , tyChar = tCh

                   , tyBool = tB
                   , dcTrue = dcT
                   , dcFalse = dcF

                   , tyRational = tR

                   , tyList = tList
                   , dcCons = tCons
                   , dcEmpty = tEmp

                   , tyMaybe = tMaybe
                   , dcJust = dJust
                   , dcNothing = dNothing

                   , tyUnit = tUnit
                   , dcUnit = dUnit

                   , eqTC = eqT
                   , numTC = numT
                   , ordTC = ordT
                   , integralTC = integralT
                   , realTC = realT
                   , fractionalTC = fractionalT

                   , integralExtactReal = integralEReal
                   , realExtractNum = realENum
                   , realExtractOrd = realEOrd
                   , ordExtractEq = ordEEq

                   , eqFunc = eqF
                   , neqFunc = neqF

                   , plusFunc = plF
                   , minusFunc = minusF
                   , timesFunc = tmsF
                   , divFunc = divF
                   , negateFunc = negF
                   , modFunc = modF

                   , fromIntegerFunc = fromIntegerF
                   , toIntegerFunc = toIntegerF

                   , toRatioFunc = toRatioF
                   , fromRationalFunc = fromRationalF

                   , geFunc = geF
                   , gtFunc = gtF
                   , ltFunc = ltF
                   , leFunc = leF

                   , impliesFunc = impF
                   , iffFunc = iffF

                   , andFunc = andF
                   , orFunc = orF
                   , notFunc = notF

                   , errorFunc = errF
                   , errorEmptyListFunc = errEmpListF
                   , errorWithoutStackTraceFunc = errWOST
                   , patErrorFunc = patE
                   }) =
                    (KnownValues {
                          dcInt = rename old new dI
                        , dcFloat = rename old new dF
                        , dcDouble = rename old new dD
                        , dcInteger = rename old new dI2
                        , dcChar = rename old new dcCh

                        , tyInt = rename old new tI
                        , tyFloat = rename old new tF
                        , tyDouble = rename old new tD
                        , tyInteger = rename old new tI2
                        , tyChar = rename old new tCh

                        , tyBool = rename old new tB
                        , dcTrue = rename old new dcT
                        , dcFalse = rename old new dcF

                        , tyRational = rename old new tR
                        
                        , tyList = rename old new tList
                        , dcCons = rename old new tCons
                        , dcEmpty = rename old new tEmp

                        , tyMaybe = rename old new tMaybe
                        , dcJust = rename old new dJust
                        , dcNothing = rename old new dNothing

                        , tyUnit = rename old new tUnit
                        , dcUnit = rename old new dUnit

                        , eqTC = rename old new eqT
                        , numTC = rename old new numT
                        , ordTC = rename old new ordT
                        , integralTC = rename old new integralT
                        , realTC = rename old new realT
                        , fractionalTC = rename old new fractionalT

                        , integralExtactReal = rename old new integralEReal
                        , realExtractNum = rename old new realENum
                        , realExtractOrd = rename old new realEOrd
                        , ordExtractEq = rename old new ordEEq

                        , eqFunc = rename old new eqF
                        , neqFunc = rename old new neqF

                        , plusFunc = rename old new plF
                        , minusFunc = rename old new minusF
                        , timesFunc = rename old new tmsF
                        , divFunc = rename old new divF
                        , negateFunc = rename old new negF
                        , modFunc = rename old new modF

                        , fromIntegerFunc = rename old new fromIntegerF
                        , toIntegerFunc = rename old new toIntegerF

                        , toRatioFunc = rename old new toRatioF
                        , fromRationalFunc = rename old new fromRationalF

                        , geFunc = rename old new geF
                        , gtFunc = rename old new gtF
                        , ltFunc = rename old new ltF
                        , leFunc = rename old new leF
          
                        , impliesFunc = rename old new impF
                        , iffFunc = rename old new iffF

                        , andFunc = rename old new andF
                        , orFunc = rename old new orF
                        , notFunc = rename old new notF

                        , errorFunc = rename old new errF
                        , errorEmptyListFunc = rename old new errEmpListF
                        , errorWithoutStackTraceFunc = rename old new errWOST
                        , patErrorFunc = rename old new patE
                        })

instance Named a => Named [a] where
    {-# INLINE names #-}
    names = foldMap names
    {-# INLINE rename #-}
    rename old new = fmap (rename old new)
    {-# INLINE renames #-}
    renames hm = fmap (renames hm)

instance Named a => Named (S.Seq a) where
    {-# INLINE names #-}
    names = foldMap names
    {-# INLINE rename #-}
    rename old new = fmap (rename old new)
    {-# INLINE renames #-}
    renames hm = fmap (renames hm)


instance Named a => Named (Maybe a) where
    {-# INLINE names #-}
    names = foldMap names
    {-# INLINE rename #-}
    rename old new = fmap (rename old new)
    {-# INLINE renames #-}
    renames hm = fmap (renames hm)

instance Named a => Named (M.Map k a) where
    {-# INLINE names #-}
    names = foldMap names
    {-# INLINE rename #-}
    rename old new = fmap (rename old new)
    {-# INLINE renames #-}
    renames hm = fmap (renames hm)

instance Named a => Named (HM.HashMap k a) where
    {-# INLINE names #-}
    names = foldMap names
    {-# INLINE rename #-}
    rename old new = fmap (rename old new)
    {-# INLINE renames #-}
    renames hm = fmap (renames hm)

instance Named () where
    {-# INLINE names #-}
    names _ = S.empty
    {-# INLINE rename #-}
    rename _ _ = id
    {-# INLINE renames #-}
    renames _ = id

instance (Named s, Hashable s, Eq s) => Named (HS.HashSet s) where
    {-# INLINE names #-}
    names = names . HS.toList
    {-# INLINE rename #-}
    rename old new = HS.map (rename old new)
    {-# INLINE renames #-}
    renames hm = HS.map (renames hm)

instance (Named a, Named b) => Named (a, b) where
    names (a, b) = names a <> names b
    rename old new (a, b) = (rename old new a, rename old new b)
    renames hm (a, b) = (renames hm a, renames hm b)

instance (Named a, Named b, Named c) => Named (a, b, c) where
    names (a, b, c) = names a <> names b <> names c
    rename old new (a, b, c) = (rename old new a, rename old new b, rename old new c)
    renames hm (a, b, c) = (renames hm a, renames hm b, renames hm c)

instance (Named a, Named b, Named c, Named d) => Named (a, b, c, d) where
    names (a, b, c, d) = names a <> names b <> names c <> names d
    rename old new (a, b, c, d) = (rename old new a, rename old new b, rename old new c, rename old new d)
    renames hm (a, b, c, d) = (renames hm a, renames hm b, renames hm c, renames hm d)

instance (Named a, Named b, Named c, Named d, Named e) => Named (a, b, c, d, e) where
    names (a, b, c, d, e) = names a <> names b <> names c <> names d <> names e
    rename old new (a, b, c, d, e) = (rename old new a, rename old new b, rename old new c, rename old new d, rename old new e)
    renames hm (a, b, c, d, e) = (renames hm a, renames hm b, renames hm c, renames hm d, renames hm e)

instance Named Bool where
    {-# INLINE names #-}
    names _ = S.empty
    {-# INLINE rename #-}
    rename _ _ = id

instance Named Int where
    {-# INLINE names #-}
    names _ = S.empty
    {-# INLINE rename #-}
    rename _ _ = id

instance Named Integer where
    {-# INLINE names #-}
    names _ = S.empty
    {-# INLINE rename #-}
    rename _ _ = id

instance Named T.Text where
    {-# INLINE names #-}
    names _ = S.empty
    {-# INLINE rename #-}
    rename _ _ = id

instance (Named k, Named v, Eq k, Hashable k) => Named (UF.UFMap k v) where
    names = names . UF.toList
    rename old new = UF.fromList . rename old new . UF.toList
    renames hm = UF.fromList . renames hm . UF.toList


freshSeededString :: T.Text -> NameGen -> (Name, NameGen)
freshSeededString t = freshSeededName (Name t Nothing 0 Nothing)

freshSeededStrings :: [T.Text] -> NameGen -> ([Name], NameGen)
freshSeededStrings t = freshSeededNames (map (\t' -> Name t' Nothing 0 Nothing) t)

freshSeededName :: Name -> NameGen -> (Name, NameGen)
freshSeededName (Name n m _ l) (NameGen hm) =
    (Name n m i' l, NameGen hm')
    where 
        i' = HM.lookupDefault 0 (n, m) hm
        hm' = HM.insert (n, m) (i' + 1) hm

freshSeededNames :: [Name] -> NameGen -> ([Name], NameGen)
freshSeededNames ns ng = swap $ mapAccumR (\ng' n -> swap $ freshSeededName n ng') ng ns

freshName :: NameGen -> (Name, NameGen)
freshName ngen = freshSeededName seed ngen
  where
    seed = Name "fs" Nothing 0 Nothing

freshNames :: Int -> NameGen -> ([Name], NameGen)
freshNames i ngen = freshSeededNames (replicate i (Name "fs" Nothing 0 Nothing)) ngen

freshId :: Type -> NameGen -> (Id, NameGen)
freshId = freshSeededId (Name "fs" Nothing 0 Nothing)

freshIds :: [Type] -> NameGen -> ([Id], NameGen)
freshIds ts ngen = 
    let
        (ns, ngen') = freshNames (length ts) ngen
    in
    (map (uncurry Id) (zip ns ts), ngen')
    
freshSeededId :: Named a => a -> Type -> NameGen -> (Id, NameGen)
freshSeededId x t ngen =
    let
        (n, ngen') = case S.viewl (names x) of
                            S.EmptyL -> error "freshSeededId: no names available"
                            x' S.:< _ -> freshSeededName x' ngen
    in
    (Id n t, ngen')

freshVar :: Type -> NameGen -> (Expr, NameGen)
freshVar t ngen =
    let
        (i, ngen') = freshId t ngen
    in
    (Var i, ngen')

-- | Allows mapping, while passing a NameGen along
mapNG :: (a -> NameGen -> (b, NameGen)) -> [a] -> NameGen -> ([b], NameGen)
mapNG f xs ng = swap $ mapAccumR (\xs' ng' -> swap $ f ng' xs') ng xs
{-# INLINE mapNG #-}