{---------------------------------------------------------------
--
-- 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, [])
}