
--  Andrebbero aggiunti commenti adeguati al codice


data ST =  Var Int | Arrow ST ST
      deriving(Show)


type Substs = ST -> ST 



occur (Var n) (Var m) = (n == m)
occur (Var n) (Arrow t1 t2) = (occur (Var n) t1) || (occur (Var n) t2)
occur t1 t2 = error "First arg of occur must be a variable"


identity x = x


mksubst :: ST -> ST -> Substs
mksubst (Var n) t  (Var m) = if (m == n) then t
                                         else (Var m)
mksubst (Var n) t (Arrow t1 t2) = (Arrow (mksubst (Var n) t t1) (mksubst (Var n) t t2))
mksubst t1 t2 t3 = error "First arg of mksubst must be a variable"


compose f g = \x -> (f (g x))


unify :: ST -> ST -> Substs

 
unify (Var n) t
      | ((Var n) == t)           = identity
      | (not (occur (Var n) t))  = (mksubst (Var n) t)
      | otherwise                = error "Error during type unification!"


unify t (Var n) = (unify (Var n) t)

unify (Arrow t1 t2) (Arrow t3 t4) = let s1 = (unify t1 t3) 
                                        s2 = (unify (s1 t2) (s1 t4))
                                    in (compose s1 s2)

----Altra possibile soluzione


--mancano completamente i commenti al codice --



data TypeVar = TypeVar Int

instance Show TypeVar
   where show (TypeVar n) = "V"++(show n)

instance Eq TypeVar
   where  TypeVar n == TypeVar m = (n == m)



data SimpleType = VarType TypeVar | ArrowType SimpleType SimpleType
   

instance Show SimpleType
   where show (VarType vt) = show vt
         show (ArrowType t1 t2) = "("++(show t1)++" -> "++(show t2)++")"

type TypeSubst = (SimpleType -> SimpleType)




compose f g x = f (g x) 



occur ::  TypeVar -> SimpleType -> Bool

occur tvar1 (VarType tvar2) = tvar1==tvar2

occur tvar (ArrowType type1 type2) = (occur tvar type1) || (occur tvar type2)



pair2TypeSubst :: (TypeVar,SimpleType) -> TypeSubst


pair2TypeSubst (tvar1,tp) (VarType vtype) =  if (vtype == tvar1) 
                                                then tp
                                                else (VarType vtype)

pair2TypeSubst (tvar1,tp) (ArrowType t1 t2) = (ArrowType (pair2TypeSubst (tvar1,tp) t1) (pair2TypeSubst (tvar1,tp) t2))


identity x = x





unifyTypes :: SimpleType -> SimpleType -> TypeSubst


unifyTypes (VarType vtype1) (VarType vtype2) = if (vtype1 == vtype2)
                                                  then identity
                                                  else pair2TypeSubst (vtype1, (VarType vtype2))


unifyTypes (VarType vtype) tp = if (occur vtype tp)
                                    then  error "Not Unifiable Types"
                                    else pair2TypeSubst (vtype,tp)

unifyTypes tp (VarType vtype) = unifyTypes (VarType vtype) tp

unifyTypes (ArrowType t1 t2) (ArrowType t3 t4) = (compose s2 s1)
                                                   where s1 = (unifyTypes t1 t3)
                                                         s2 = unifyTypes (s1 t2) (s1 t4)






--Examples:

myType1 = ArrowType (VarType (TypeVar 1)) (VarType (TypeVar 1))

myType2 = ArrowType 
                   (ArrowType 
                            (VarType (TypeVar 2)) 
                            (VarType (TypeVar 2))
                    ) 
                   (ArrowType 
                            (VarType (TypeVar 3)) 
                            (ArrowType 
                                     (VarType (TypeVar 3)) 
                                     (VarType (TypeVar 2))
                             )
                    )

myType3 = ArrowType (ArrowType (VarType (TypeVar 1)) (VarType (TypeVar 2))) (VarType (TypeVar 1))

myType4 = ArrowType (ArrowType (VarType (TypeVar 3)) (VarType (TypeVar 2))) (VarType (TypeVar 1))


substA = unifyTypes myType1 myType2


substB = unifyTypes myType2 myType4

myType5 = substB myType2 

-- Notice that with our program it is not possible to know that myType1 and myType3 are not unifyable
-- before substB is applyied to an argument















