--

import ST

---------------------------------------------
-- Some Monad type constructors

data Id x = Id x 
data Exception x = Ok x | Fail
data Env e x = Env (e -> x)
data Mult x = Mult [x]
data Store s x = St(s -> (x,s)) 
data Output x = OP(x,String)

-- Some Instance declarations

instance Monad Id where
  return x = Id x
  (>>=) (Id x) f = f x
 
instance Monad Exception where
  return x = Ok x
  (>>=) (Ok x) f = f x
  (>>=) Fail f = Fail
 
instance Monad (Env e) where
  return x = Env(\ e -> x)
  (>>=) x yf = let Env f = x
                   h e = let x = f e
                             Env g = yf x
                         in g e
               in Env h 

instance Monad (Store s) where
  return x = St(\ s -> (x,s))
  (>>=) x yf = let St f = x
                   h s1 = let (x,s2) = f s1
                              St g = yf x
                              (y,s3) = g s2
                          in (y,s3)
               in St h
               
instance Monad Mult where
  return x = Mult[x]
  (>>=) x yf = let Mult xs = x
                   ys = map yf xs
               in Mult (concat[ z | Mult z <- ys ])
               
instance Monad Output where
  return x = OP(x,"")
  (>>=) (OP(x,s1)) f = let OP(y,s2) = f x in OP(y,s1 ++ s2)               

--- A few non-standard morphisms (the interface of the ADT)

type Map = [(String,Value)]   

getEnv :: String -> (Env Map Value)
getEnv nm = Env(\ s -> find s)
  where find [] = error ("Name: "++nm++" not found")
        find ((s,n):m) = if s==nm then n else find m

runInNewEnv :: String -> Int -> (Env Map Value) -> (Env Map Value)
runInNewEnv s n (Env g) = Env(\ m -> g ((s,n):m))
                                    
getStore :: String -> (Store Map Value)
getStore nm = St(\ s -> find s s)
  where find w [] = (0,w)
        find w ((s,n):m) = if s==nm then (n,w) else find w m

putStore :: String -> Value -> (Store Map ())
putStore nm n = (St(\ s -> ((),build s)))
  where build [] = [(nm,n)]
        build ((s,v):zs) = 
           if s==nm then (s,n):zs else (s,v):(build zs)
 
printOutput:: String -> Output ()
printOutput s = OP((),s)

-------------------------------------------------------------------
-- A much richer monad combining many of the elements of the above

type Value = Int
type Maps x = [(String,x)]
data M v x = M(Maps v -> Maps v -> (Maybe x,String,Maps v))

instance Monad (M v) where
 return x = M(\ st env -> (Just x,[],st))
 (>>=) (M f) g = M h
   where h st env = compare env (f st env)
         compare env (Nothing,op1,st1)  = (Nothing,op1,st1)
         compare env (Just x, op1,st1)  = next env op1 st1 (g x)
         next env op1 st1 (M f2)        = compare2 op1 (f2 st1 env)
         compare2 op1 (Nothing,op2,st2) = (Nothing,op1++op2,st2)
         compare2 op1 (Just y, op2,st2) = (Just y, op1++op2,st2)


--- Sub classes of Monad for generic evaluation


class Monad m => Eval e v m where
  eval :: e -> m v

class Monad m => Failure m where
  fails :: m a
  
class Monad m => HasEnv m v where
  inNewEnv :: String -> v -> m v -> m v
  getfromEnv :: String -> m v
  
class Monad m => HasStore m v where   
  getfromStore :: String -> m v
  putinStore :: String -> v -> m v
    
class (Monad m,Show v) => Prints m v where
  write :: String -> v -> m v  

---------------------------------------------------------------
-- Instance declarations for the rich monad

instance Failure (M v) where
  fails = M(\ st env -> (Nothing,[],st))

get name ((a,b):m) = if a==name then b else get name m

instance HasEnv (M v) v where
  inNewEnv name v (M f) = M(\ st env -> f st ((name,v):env))
  getfromEnv name = M h
     where h st env = (Just(get name env),[],st)
           
instance HasStore (M v) v where
  getfromStore name = M h
     where h st env = (Just(get name st),[],st)
  putinStore name v = M h
     where h st env = (Just v,[],(name,v):st)
     
instance Show v => Prints (M v) v where
  write message v = M h
     where h st env = (Just v,message++(show v),st)

--------------------------------------------------------------
-- A state based algorithm

data Tree a = Tip | Node (Tree a) a (Tree a)

number :: Tree a -> Int -> (Tree (a,Int), Int)
number Tip n = (Tip, n)
number (Node l x r) n = (Node l' (x,n) r', n'')
  where (l',n')  = number l (n+1)
        (r',n'') = number r n'
        
---------------------------------------------------------------
-- Another example

data Aop = Multiply | Add | Sub

data Exp 
  = Num Int 
  | Var String
  | Oper (Exp, Aop, Exp )
  | Let (String, Exp, Exp)

e2 = Let("x",Num 5,
         Oper(Var "x", Add,
              Oper(Num 4,Multiply,Num 2)))
              
-- free variables

freeV :: Exp -> [ String]
freeV term = freevars [] term []
 where
  freevars bound x free = case x of
    Num(i) -> free
    Var(s) ->  if any (==s) bound
                  then free else add s free  
    Oper(x,y,z) ->freevars bound x (freevars bound z free)
    Let(s,e,body) -> 
       freevars (s:bound) body (freevars bound e free)
 
add x [] = [x]
add x xs = if any (==x) xs then xs else x:xs

rename n x = x ++ (show n)

find s [] = s
find s ((t,v):xs) = if s==t then v else find s xs

renameE x = snd(rnE [] 0 x)
 where rnE bnd n x = case x of
          Num(i) ->(n,Num i)
          Var(s) -> (n,Var(find s bnd))
          Oper(x,y,z) -> let (n1,x') = rnE bnd n x
                             (n2,z') = rnE bnd n1 z
                         in (n2,Oper(x',y,z'))
          Let(s,e,b) ->  
             let s' = rename n s
                 (n1,b') = rnE ((s,s'):bnd) (n+1) b
                 (n2,e') = rnE bnd n1 e
             in (n2,Let(s',e',b'))
             
e3 = 
 Let("x", Oper(Var "z",Multiply,Num 2),
     Let("q",
         Oper(Var "x",Add,Var "w"),
         Oper(Var "x",Sub,
              Oper(Var "q",
                   Multiply,Var "v"))))


e4 = renameE e3
{- 
 Let ("x0", Oper (Var "z",Multiply,Num 2),
      Let ("q1",
          Oper (Var "x0",Add,Var "w"),
          Oper (Var "x0",Sub,
                Oper (Var "q1",
                      Multiply,Var "v"))))
-}             

------------------------------------------------------
-- State in Haskell

swap :: STRef s a -> STRef s a -> ST s ()
swap v w = do { x <- readSTRef v
              ; y <- readSTRef w
              ; writeSTRef w x
              ; writeSTRef v y
              }
              
-- The Monadic Renaming Example

rn bnd (Num i) v = return(Num i)
rn bnd (Var s)v  = return(Var(find s bnd))
rn bnd (Oper(x,y,z)) v =
   do { x' <- rn bnd x v
      ; z' <- rn bnd z v
      ; return(Oper(x',y,z')) }
rn bnd (Let(s,e,b)) v = 
   do { n <- readSTRef v
      ; writeSTRef v (n+1)
      ; s' <- return(rename n s)
      ; b' <- rn ((s,s'):bnd) b v
      ; e' <- rn bnd e v
      ; return(Let(s',e',b')) }
              

re x = runST(do { v <- newSTRef 0; rn [] x v })
   
e5 =  Let("x", Oper(Var "z",Multiply,Num 2),
          Let("q", Oper(Var "x",Add,Var "w"),
              Oper(Var "x", Sub, 
                   Oper(Var "q",Multiply,Var"v"))))
e6 = re e5
 
----------------------------------------------------------
-- Updateable linked lists

type UpdList s a = STRef s (ListVar s a, ListVar s a)

type ListVar s a = STRef s (ListElt s a)
data ListElt s a = Nil
                 | Cons a (ListVar s a)
newList :: ST s (UpdList s a)
newList = do {f <- newSTRef Nil; newSTRef (f,f)}

frontList :: UpdList s a -> ST s a
frontList l = do {(f,b) <- readSTRef l;
                  Cons x f' <- readSTRef f;
                  writeSTRef l (f',b);
                  return x}

backList :: UpdList s a -> a -> ST s ()
backList l x = do {(f,b) <- readSTRef l;
                   b' <- newSTRef Nil;
                   writeSTRef b (Cons x b');
                   writeSTRef l (f,b')}

emptyList :: UpdList s a -> ST s Bool
emptyList l = do {(f,b) <- readSTRef l; return (f == b)}
--