module Overload.General where
import Data.Functor.Identity
import Control.Effects.State
import Control.Monad
import Data.List (foldl')
import Overload.TypeTree
type VariableMapping a b = [(a, TypeTree b)]
trySetVar :: (MonadEffect (State (VariableMapping a b)) m, Eq a, Eq b) => a -> TypeTree b -> m Bool
trySetVar name typ = do
mapping <- getState
case lookup name mapping of
Just typ' | typ == typ' -> return True
| otherwise -> return False
Nothing -> do
setState ((name, typ) : mapping)
return True
isMoreGeneralThan :: forall a b. (Eq a, Eq b) => TypeTree a -> TypeTree b -> Bool
isMoreGeneralThan t1 t2 =
runIdentity (handleStateT ([] :: VariableMapping a b) (isMoreGeneralThan' t1 t2))
isMoreGeneralThan' :: (MonadEffect (State (VariableMapping a b)) m, Eq a, Eq b)
=> TypeTree a -> TypeTree b -> m Bool
isMoreGeneralThan' (Var n) t = trySetVar n t
isMoreGeneralThan' (Concrete n1) (Concrete n2) | n1 == n2 = return True
isMoreGeneralThan' (App t1 t2) (App t3 t4) =
(&&) <$> t1 `isMoreGeneralThan'` t3 <*> t2 `isMoreGeneralThan'` t4
isMoreGeneralThan' _ _ = return False
getEqualities :: forall a b. (Eq a, Eq b) => TypeTree a -> TypeTree b -> [(b, TypeTree a)]
getEqualities specific general = runIdentity $ handleStateT ([] :: VariableMapping b a) $ do
res <- general `isMoreGeneralThan'` specific
if res then getState
else error "Can't get equalities because the second type isn't more general than the first"
minimize :: Eq a => [TypeTree a] -> [TypeTree a]
minimize [] = []
minimize (t : ts) = foldl' minimizer [t] ts
where minimizer ms candidate = runIdentity $ handleStateT True $ do
ms' <- filterM (\m ->
if m `isMoreGeneralThan` candidate then setState False >> return True
else if candidate `isMoreGeneralThan` m then return False
else return True) ms
newMin <- getState
if newMin then return (candidate : ms')
else return ms'