HaRe-0.6: tools/base/TI/TiGeneralize.hs
{-+
This module defines type checking functions for generalizing inferred types
and applying context reduction (#generalise'#).
-}
module TiGeneralize where
import TiMonad
import TiTypes
import TiUtil
import TiSolve(solve,TypeConstraint(..))
import Unification(isVar)
import TiNames(ValueId,TypeVar,dictName,dictName')
import TiClasses hiding (isVar)
import TiKinds
--import TiPrelude
import TiContextReduction
import TiDefault
import TiFunDeps(predDeps,closure,improvement)
import TiFresh
import HasBaseStruct
import HsConstants(mod_Prelude,tuple)
import BaseSyntax(srcLoc,TI(..))
import TypedIds(NameSpace(..))
import PrettyPrint hiding (var)
import List((\\),partition,nub)--,intersect
import MUtils
--import Debug.Trace(trace) -- debug
{-+
If there were polymorphic kinds, #kgeneralise# would produce kind schemes,
but currently it just instantiates any remaining kind variables after kind
inference to *, in accordance with the Haskell 98 report, section 4.6.
-}
kgeneralise m =
do (kbs,s) <- getKSubst m
return $ (map.fmap) (fixkvars . ksubst s) kbs
{-+
As #kgeneralise#, but treat Pred as equal to *. Useful for kind checking
after the dictionary translation.
-}
kgeneraliseSloppy m =
do (kbs,s) <- getKSubstSloppy m
return $ (map.fmap) (apFst (fixkvars . ksubst s)) kbs
--generalise1 u = generalise' u id
--generalise u = generalise' u fmap
{-+
At the moment, #checkKinds# only keeps track of the kinds of type variables
that remain after type inference. It could be improved to also check
that all subsitutions performed were kind preserving.
-}
checkKinds kpreds = return (tyvars kpreds)
where
--tyvars :: Unifiable i => [Kinded (Type i)] -> [Kinded i]
tyvars kpreds = nub [v:>:k|t:>:k<-kpreds,Just v<-[isVar t]] -- hmm
{-
catchAmbiguity dicts =
if null dicts
then done
else fail $ pp ("Unresolved overloading: "<+>ppDicts dicts)
-}
{-+ Iterate context reduction and improvement until nothing changes -}
reduceAndImprove dicts0 =
do (dicts1,r1) <- contextReduction dicts0
subst1@(S s) <- errorContext "Applying functional dependencies" $
improvement [ p | d:>:p<-dicts1]
if null s
then return (dicts1,r1,subst1)
else do --trace (pp $ "Improvement:"<+>vcat [ppi s,"of"<+>ppi dicts1]) $ done
(dicts2,r2,subst2) <- reduceAndImprove (apply subst1 dicts1)
return (dicts2,r2 . r1,compS subst2 subst1)
{-+ Generalize inferred types: -}
generalise' keepambig unrestricted fmap' m =
do env <- getTEnv
(x:>:t0,res@(dicts0,kpreds0,subst0)) <- getSubst m
--trace (pp (t0,res)) $ return ()
((dicts,r,subst1),([],kpreds1,S [])) <- getSubst $ reduceAndImprove dicts0
let subst@(S s) = compS subst1 subst0
kpreds <- checkKinds (kpreds1++[apply subst1 t:>:k|t:>:k<-kpreds0])
--trace (pp kpreds) $ return ()
mono <- monomorphism # getEnv -- The monomorphism restriction is optional!
fdeps <- predDeps [ p | d:>:p<-dicts]
let t = apply subst t0
ngvs0 = tv env -- could probably be made more efficient...
s' = [s1|s1@(v,_)<-s,v `elem` ngvs0]
-- Non-generic variables by the usual Hindley-Milner rules:
hmngvs0 = ngvs0++tv (map snd s')
-- Non-generic vars when taking functional dependencies into account:
hmngvs = closure fdeps hmngvs0
-- (deferred,retained) predicates:
(ngdicts,gdicts0) = if not mono || unrestricted
then partition ng dicts
else (dicts,[]) -- monomorphism restriction applies
where ng = all (`elem` hmngvs) . tv
mrngvs = tv ngdicts
ngvs = hmngvs++ -- the usual Hindley-Milner restriction
mrngvs -- from the monomorphism restriction
(ngkpreds,gkpreds0) = partition ng kpreds
where ng (v:>:k) = v `elem` ngvs -- mrngvs is not enough!
--(avs,(ambigdicts,gdicts)) = ambiguities ngvs gdicts0 t
--catchAmbiguity ambigdicts; let subst' = idS; r' = id
(gdicts,(subst',r')) <-
let known = (if keepambig then tdom gkpreds0 else [])++{-hm-}ngvs
in resolveAmbiguities fdeps known gdicts0 t
gkpreds <- checkKinds [varSubst subst' v:>:k|v:>:k<-gkpreds0]
let ds:>:ctx = unzipTyped gdicts
gvs = tdom gkpreds
ks = map (emap HsVar) gkpreds
gen t = Forall ags (kinded ks gs) qt
where ags = filter (\ (v:>:_)->v `notElem` gs) gkpreds
qt = ctx:=>t
gs = tv qt \\ ngvs -- normal type scheme
--gs = gvs \\ ngvs -- keep "ambiguous" type variables
sc = fmap' gen t
mapM_ constrain [tyvar v:=:t|(v,t)<-s'] -- deferred equality constraints
mapM_ addinst ngdicts -- deferred predicates
--trace (pp $ "ngkpreds:"<+>ngkpreds) $ return ()
mapM_ addkinst [tyvar v:>:k|v:>:k<-ngkpreds] -- what to propagate?
--abstract ds (r (apply subst x))>:fmap' gen t
(ds,r' $ apply subst' $ r $ apply subst x)>:sc
getSubst m = do env <- getKEnv ; getSubst' (solve env) m
getKSubst = getSubst' ksolve
getKSubstSloppy = getSubst' ksolveSloppy
getSubst' solve m =
do (ans,cs) <- getConstraints m
subst <- solve cs
return (ans,subst)