{---------------------------------------------------------------
 --
 -- Unify.hs : contains function definition of an unification
 --		algorithm
 -- T.Yu@cs.ucl.ac.uk	September 25, 1997
 --
 --------------------------------------------------------------}

module Unify(applySub,applySubToExp,unify,xoverUnify, Theta) where
import Header(TypeExp(..),Expression(..),ParseTree(..))
import NonStdTrace

-- type synonyms ------------------------------------------------------------------
--
-- Theta: a substituation set which binds type variables to type expression.

type Theta = [(String, TypeExp)]

-- replaceVar function ----------------------------------------------------------------
--
-- This function takes 2 arguments : a type variable string and theta.
-- It returns the TypeExp which the type variable is bound in
-- the theta. If the variable string is not bounded in the theta,
-- it return itself. (i.e. no replacement)
-- This function is called from applySub function.

replaceVar :: String -> Theta -> TypeExp

replaceVar var theta = case theta of
		       {
			[] -> TypeVar var;   -- no replacement
			((x,value):rest) -> if var == x then 
						value
				  	    else replaceVar var rest
			}

--replaceDummy function ----------------------------------------------------------------
--
-- This function takes two arguments :a dummy type variable string and theta.
-- It returns the TypeExp which the type variable is bound in
-- the theta.If the variable string is not bounded in the theta,
-- it return itself. (i.e. no replacement)
-- This function is called from applySub function.


replaceDummy :: String -> Theta -> TypeExp

replaceDummy var theta = case theta of
			{
			  [] -> DummyType var;   -- no replacement
			  ((x,value):rest) -> if var == x then 
						value
					      else replaceDummy var rest
			}

-- applySubToTree -------------------------------------------------------------------------
--
applySubToTree :: ParseTree -> Theta -> ParseTree

applySubToTree tree theta = case tree of 
			    {
				Empty -> tree ;
				(ExpCons exp) -> (ExpCons (applySubToExp exp theta))
			    }



-- applySubToExp -------------------------------------------------------------------------
--

applySubToExp :: Expression -> Theta -> Expression

applySubToExp exp theta = case exp of
			{
			  (Application exp1 exp2 typeExp) ->
				Application (applySubToExp exp1 theta)
				(applySubToExp exp2 theta)(applySub theta typeExp);
			  _ -> exp
			}

-- applySub function ---------------------------------------------------------------------
--
-- This function takes 2 arguments : theta and a type Expression.
-- It applies substituation by replacing all type variables appeared
-- in the Type Expression with new Term which is bound in theta.

applySub :: Theta -> TypeExp -> TypeExp

applySub theta typeExp = case typeExp of
			{
			 (TypeVar v) -> replaceVar v theta;
			 (DummyType v) -> replaceDummy v theta;
			 (ListType t) -> ListType (applySub theta t);
			 (Arrow t1 t2) -> Arrow (applySub theta t1) (applySub theta t2);
			 (Brackets e) -> Brackets (applySub theta e);
			 _ -> typeExp -- no substituation takes place
			}

-- substitute function --------------------------------------------------------------------
--
-- This function takes 3 arguments : a type variable, a new typeExp to substitute
-- the type variable and a type expression where substituation is
-- taking place. It returns the new type expression which substituation
-- has been done.
-- For every typeVar in TypeExp, replace it with newTerm

substitute :: String -> TypeExp -> TypeExp -> TypeExp

substitute typeVar newTerm typeExp = 

	case typeExp of
        {
	(TypeVar q)               -> if typeVar == q 
                                     then newTerm
		                     else 
			             typeExp; -- no substituation take place
	(ListType typeExp1)       -> ListType (substitute typeVar newTerm typeExp1);
	(Arrow typeExp1 typeExp2) -> Arrow (substitute typeVar newTerm typeExp1) 
					   (substitute typeVar newTerm typeExp2);
	(Brackets typeExp1)       -> Brackets (substitute typeVar newTerm typeExp1);
	_                         -> typeExp -- no substituation take place
        }

-- member function ----------------------------------------------------------------------------
--
-- This function takes two arguments: a variable and a Type Expression.
-- If the variable appears in the Type Expression, if returns Trues.
-- Otherwise, it returns False. This function is only used to check 
-- whether a temporary type variable exists in the type expression.
-- A dummy type variable can't never be unify with another dummy type.

member :: String -> TypeExp -> Bool

member var typeExp = case typeExp of
		    {
			(TypeVar tVar) -> if var == tVar then 
						True 
			    		  else False;
			(Arrow pType qType) -> member var pType || member var qType;
			(ListType lType) -> member var lType;
			(Brackets exp) -> member var exp;
			_ -> False  -- this takes care of DummyType
		     }

-- unify function ----------------------------------------------------------------------------------
--
-- This function takes a list of Type Expression Pairs, a Theta (binding of temporary type variables
-- or binding of dummy type varialbes). It unifies each pair in the list and returns a new Theta.
-- This function is used in two separate occasions: to bind dummy type variables in a polymorphic
-- function and to bind temporary type variables in the parse tree. The two occassions are handled
-- separately. Hence 2. & 4. can't never be used at the same time.

unify :: Bool -> [(TypeExp , TypeExp)] -> Theta -> (Bool, Theta)

unify unifiable typeExps theta =
  if not unifiable then
	(False, theta)
  else
	case typeExps of
	{
	 [] -> (True, theta);

	 --2. binding temporary type variables. can handle exp is temporary type variables.
	 ((s@(TypeVar v),exp):rest) ->
		if exp == s then 
			unify True rest theta 
		else if member v exp then
			(False,[])
		else 
       			let newRest = map (\(l, r) -> (substitute v exp l, substitute v exp r)) rest
		    	    newTheta = map (\(name, def) -> (name,substitute v exp def)) theta
		    	    {- substitute type variable V with Exp in the stack (rest) and in theta -}
			in
		    	    unify True newRest ((v,exp):newTheta); -- add v=exp to theta;
	 --3. in case s is instantiated through a dummy type variable and length(exp) > 1
	 ((exp, s@(TypeVar v)):rest) ->
		unify True ((s, exp):rest) theta;

	 --4. binding dummy type variables. exp can never be temporary/dummy type variables.
	 ((exp, s@(DummyType v)):rest) ->
		let newRest = map (\(l, r) -> (substitute v exp l, substitute v exp r)) rest
		    newTheta = map (\(name, def) -> (name,substitute v exp def)) theta
				{- substitute type variable V with Exp in the stack (rest) and in theta -}
		in
	    	    unify True newRest ((v,exp):newTheta); -- add v=exp to theta;

	 ((Arrow t1 t2, Arrow t3 t4):rest) ->
		unify True ((t1,t3):(t2,t4):rest) theta; -- this handle higher-order function as terminal

	 ((Brackets t1, Brackets t2):rest) ->
		unify True ((t1, t2):rest) theta;

	 ((ListType t1, ListType t2):rest) ->
		unify True ((t1,t2):rest) theta;

	 ((t1,t2):rest) -> 
		if t1 == t2 then 
			unify True rest theta
		else (False, [])
	}	

-- xoverUnify function -------------------------------------------------------------------
-- For xover nodes unification, Type variable can't unify with Function type.
-- Both application nodes and lambda nodes have to have the same number of 
-- args to be able to perform crossover.

xoverUnify :: Bool -> [(TypeExp, TypeExp)] -> Theta -> (Bool,Theta)

xoverUnify unifiable typeExps theta =
  if not unifiable then
	(False, theta)
  else
	case typeExps of
	{
	 [] -> (True, theta);

	 ((TypeVar v,Arrow t1 t2):rest) -> (False, theta);

	--2. binding temporary type variables. can handle exp is temporary type variables.
	 ((s@(TypeVar v),exp):rest) ->
		if exp == s then 
			xoverUnify True rest theta 
		else if member v exp then
			(False,[])
		else 
       			let newRest = map (\(l, r) -> (substitute v exp l, substitute v exp r)) rest
		    	    newTheta = map (\(name, def) -> (name,substitute v exp def)) theta
		    	    {- substitute type variable V with Exp in the stack (rest) and in theta -}
			in
		    	    xoverUnify True newRest ((v,exp):newTheta); -- add v=exp to theta;
	--3. in case s is instantiated through a dummy type variable and length(exp) > 1
	 ((exp, s@(TypeVar v)):rest) ->
		xoverUnify True ((s, exp):rest) theta;

	 ((Arrow t1 t2, Arrow t3 t4):rest) ->
		xoverUnify True ((t1,t3):(t2,t4):rest) theta; -- this handle higher-order function as terminal & crossover operation

	 ((Brackets t1, Brackets t2):rest) ->
		xoverUnify True ((t1, t2):rest) theta;

	 ((ListType t1, ListType t2):rest) ->
		xoverUnify True ((t1,t2):rest) theta;

	 ((t1,t2):rest) -> 
		if t1 == t2 then 
			xoverUnify True rest theta
		else 
			(False, [])
	}	




