module FreeC.IR.TypeScheme
  ( 
    instantiateTypeScheme
  , instantiateTypeScheme'
    
  , abstractTypeScheme
  , abstractTypeScheme'
  ) where
import           Data.Composition        ( (.:) )
import           Data.List               ( (\\), partition )
import           Data.Maybe              ( fromJust )
import           FreeC.Environment.Fresh
import           FreeC.IR.SrcSpan
import           FreeC.IR.Subst
import qualified FreeC.IR.Syntax         as IR
import           FreeC.Monad.Converter
instantiateTypeScheme :: IR.TypeScheme -> Converter IR.Type
instantiateTypeScheme = fmap fst . instantiateTypeScheme'
instantiateTypeScheme' :: IR.TypeScheme -> Converter (IR.Type, [IR.Type])
instantiateTypeScheme' (IR.TypeScheme _ typeArgs typeExpr) = do
  (typeArgs', subst) <- renameTypeArgsSubst typeArgs
  let typeExpr' = applySubst subst typeExpr
      typeVars' = map IR.typeVarDeclToType typeArgs'
  return (typeExpr', typeVars')
abstractTypeScheme :: [IR.QName] -> IR.Type -> IR.TypeScheme
abstractTypeScheme = fst .: abstractTypeScheme'
abstractTypeScheme' :: [IR.QName] -> IR.Type -> (IR.TypeScheme, Subst IR.Type)
abstractTypeScheme' ns t
  = let vs         = map (fromJust . IR.identFromQName) ns
        (ivs, uvs) = partition IR.isInternalIdent vs
        vs'        = uvs ++ take (length ivs) (map makeTypeArg [0 ..] \\ uvs)
        ns'        = map (IR.UnQual . IR.Ident) (uvs ++ ivs)
        ts         = map (IR.TypeVar NoSrcSpan) vs'
        subst      = composeSubsts (zipWith singleSubst ns' ts)
        t'         = applySubst subst t
    in (IR.TypeScheme NoSrcSpan (map (IR.TypeVarDecl NoSrcSpan) vs') t', subst)
 where
  makeTypeArg :: Int -> IR.TypeVarIdent
  makeTypeArg = (freshTypeArgPrefix ++) . show