--

class  Add arg1 arg2 res | arg1 arg2 -> res where
  plus :: arg1 -> arg2 -> res

instance  Add Int Int Int  where
  plus = primIntAdd

instance  Add Float Float Float  where
  plus = primFloatAdd

instance  Add Int Float Float  where
  plus x y = (int2Float x) + y

----------------------------------
primFloatAdd :: Float -> Float -> Float
primFloatAdd x y = x+y

primIntAdd :: Int -> Int -> Int
primIntAdd x y = x+y

int2Float :: Int -> Float
int2Float x = fromInt x

--------------------------------------------
-- Implicit parameters

data Term
  = Int Int
  | Plus Term Term
  | Let String Term Term
  | Var String


eval :: (String -> Int) -> Term -> M Int
eval env t = case t of
  Int n -> return n
  Var s -> return(env s)
  Plus x y -> do { a <- eval env x
                 ; b <- eval env y
                 ; return(a+b)}
  Let s x y ->  do { a <- eval env x
                   ; eval (newenv a) y }
    where newenv a z = if s==z then a else env z

-----------------------------------------------------------

instance Show Term where
  show (Int n) = show n
  show (Var x) = x
  show (Plus x y) = "("++(show x)++"+"++(show y)++")"
  show (Let s x y) = "let "++s++" = "++show x++" in "++show y


data M a = M(a,String)
instance Monad M where
  return x = M(x,"")
  (>>=) (M(a,s1)) f = M(case f a  of
                         M(b,s2) -> (b,s1++s2))

pp :: String -> M ()
pp s = M((),s)

instance Show a => Show(M a) where
  show (M(x,y)) = show x ++ "\n   "++show y

env "y" = 1
env y = error ("Not known: "++y)

ex1 = Let "x"
          (Plus (Int 3) (Var "y"))
          (Plus (Var "x") (Plus (Int 5) (Var "y")))

copies 0 s = ""
copies n s = s++(copies (n-1) s)

eval2 :: (?depth :: Int) => (String -> Int) -> Term -> M Int
eval2 env t =
 do { pp (copies (?depth * 3) " ")
    ; pp ("=> "++(show t))
    ; pp "\n"
    ; ans <- (case t of
               Int n -> return n
               Var s -> return(env s)
               Plus x y -> do { a <- eval2 env x
                              ; b <- eval2 env y
                              ; return(a+b)}
               Let s x y ->  do { a <- eval2 env x
                                ; eval2 (newenv a) y }
                   where newenv a z = if s==z then a else env z)
              with ?depth = ?depth + 1
    ; pp (copies (?depth * 3) " ")
    ; pp ("<= "++(show ans))
    ; pp "\n"
    ; return ans
    }

putM :: M a -> IO ()
putM (M(_,s)) = putStrLn s

a1 = putM (eval2 env ex1 with ?depth = 0)

entering :: (?depth :: Int, Show b) => b -> M ()
entering t =
  do { pp (copies (?depth * 3) " ")
     ; pp ("=> "++(show t))
     ; pp "\n"
     }

leaving :: (?depth :: Int, Show b) => b -> M ()
leaving ans =
 do { pp (copies (?depth * 3) " ")
    ; pp ("<= "++(show ans))
    ; pp "\n"
    }

--trace :: (?depth :: Int) => (?depth::Int => Term -> M Int) -> Term -> M Int
trace f t =
 do { entering t
    ; let d = ?depth
    ; pp "AA"
    ; pp (show d)
    ; pp "ZZ"
    ; ans <- (f t) with ?depth = d + 1
    ; leaving ans
    ; return ans
    }


--eval3 :: (?depth :: Int) => ([Char] -> Int) -> Term -> M Int
eval3 env t = trace eval3f t
  where eval3f (Int n) = return n
        eval3f (Var s) = return(env s)
        eval3f (Plus x y) =
           do { a <- eval3 env x
              ; b <- eval3 env y
              ; return(a+b)}
        eval3f (Let s x y) =
           do { a <- eval3 env x
              ; eval3 (newenv a) y }
         where newenv a z = if s==z then a else env z


a2 = putM ((eval3 env ex1) with ?depth = 0)

--