import Data.Maybe
import Control.Monad
import Data.Map.Lazy
import Prelude hiding (lookup)


newtype Reader e a = Reader { runReader :: (e -> a) }


instance Functor (Reader e) where
    fmap = liftM

instance Applicative (Reader e) where
    pure  a  = Reader  $ \e -> a
    (<*>)    = ap


instance Monad (Reader e) where 
    return a          = Reader  $ \e -> a 
    (Reader r) >>= f  = Reader  $ \e -> runReader (f (r e)) e



ask :: Reader e e
ask       = Reader id 

local :: (e -> e) -> Reader e a -> Reader e a 
local f c = Reader $ \e -> runReader c (f e) 


type ID = String

data Exp  =  Num Int | Add Exp Exp | Var ID | Let ID Exp Exp   

eval :: Exp -> Reader (Map ID Int) Int
eval (Num n)       =  return n
eval (Add e e')    =  do  a <- eval e
                          b <- eval e'
                          return (a + b)
eval (Var v)       =  do  s <- ask 
                          return (fromJust $ lookup v s)
eval (Let v e b)   =  do  a <- eval e
                          local (insert v a) (eval b) 


evalAmb :: Exp -> Map ID Int -> Int
evalAmb = runReader . eval

amb :: Map ID Int
amb = insert "x" 5 empty

ejemplo1 = Add (Num 8) (Var "x")

ejemplo2 = Add (Num 2) (Let "x" (Num 3) ejemplo1)