module Robinson where

type TCon  = (String,Int) -- Name, Stelligkeit
data Types = TVar String 
           | SType TCon [Types]
           | FType Types Types
           deriving Eq
type Subst = [(String,Types)]

-- zentrale Unifikationsfunktion
-- Ausgabe Nothing bedeutet fail
unify :: Types -> Types -> Maybe Subst
unify (TVar cs) t 
    | occursNot cs t = Just [(cs,t)]
    | otherwise      = Nothing
unify t (TVar cs) 
    | occursNot cs t = Just [(cs,t)]
    | otherwise      = Nothing
unify (SType tcon1 ts1) (SType tcon2 ts2)
    | tcon1 == tcon2 = unifylist ts1 ts2 []
    | otherwise      = Nothing
unify (FType t11 t12) (FType t21 t22) 
                     = unifylist [t11,t12] [t21,t22] []
unify _ _            = Nothing


-- Unifikation von mehreren Gleichungen
unifylist :: [Types] -> [Types] -> Subst
             -> Maybe Subst
unifylist [] [] sigma  = Just sigma
unifylist (t1:ts1) (t2:ts2) sigma 
    =  case unify (applySubst sigma t1)
                  (applySubst sigma t2) of
         Nothing       -> Nothing
         (Just sigma1) -> unifylist ts1 ts2 sigma1
                    

applySubst :: Subst -> Types -> Types
applySubst sigma (TVar cs)  
   = head ([ t | (tv,t) <- sigma, tv == cs] ++ [TVar cs])
applySubst sigma (SType tcon ts) 
   = SType tcon (map (applySubst sigma) ts)
applySubst sigma (FType t1 t2)
   = FType (applySubst sigma t1) (applySubst sigma t2)

occursNot :: String -> Types -> Bool
occursNot cs (TVar cs1)      = cs /= cs1
occursNot cs (SType tcon ts) = and (map (occursNot cs) ts)
occursNot cs (FType t1 t2)   = occursNot cs t1 && occursNot cs t2


-- Beispielaufruf
mapBsp = printResult $ unifylist 
           [ TVar "a3"
           , TVar "a1"
           , FType (TVar "a1") (FType (SType ("List",1) [TVar "a2"]) 
                                      (TVar "a3"))
           , FType (TVar "a7") (FType (SType ("List",1) [TVar "a7"]) 
                                      (SType ("List",1) [TVar "a7"]))
           ]
           [ SType ("List",1) [TVar "a4"]
           , FType (TVar "a2") (TVar "a5")
           , FType (TVar "a1") (FType (SType ("List",1) [TVar "a2"]) 
                                      (TVar "a6"))
           , FType (TVar "a5") (FType (TVar "a6") (TVar "a3"))
           ]
           []

-- Ausgabefunktionen           
printSubst  :: Subst -> String
printSubst  =  unlines . map (\ (cs,typ) -> cs ++ " |-> " ++ show typ)

instance Show Types where
   show (TVar cs)         = cs
   show (SType (cs,n) ts) = '(':cs ++ ' ' : concatMap show ts ++ ")"
   show (FType t1 t2)     = '(': show t1 ++ "->" ++ show t2 ++ ")"
    
printResult              :: Maybe Subst -> IO ()
printResult Nothing      =  putStr "Nothing" 
printResult (Just sigma) =  putStr $ printSubst sigma
                      
