packages feed

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)