+import Control.Monad.Reader
+data Bound = Int Int | Fun (Reader Env Int -> Reader Env Int)
+type Env = Char -> Bound
+
+
+env0 = \var -> error "Not found"
+insert var value e = \sought -> if sought == var then value else e sought
+
+getint :: Char -> Reader Env Int
+getint var = asks (\e -> let (Int x) = e var in x)
+
+getfun :: Char -> Reader Env (Reader Env Int -> Reader Env Int)
+getfun var = asks (\e -> let (Fun f) = e var in f)
+
+-- monadic versions of `x` and `y` and `f`
+getx = getint 'x'
+gety = getint 'y'
+getf = getfun 'f'
+
+-- monadic version of `y + x`
+expr1 :: Reader Env Int
+expr1 = liftM2 (+) gety getx
+
+-- monadic version of `\y -> y + x`
+lambda1 :: Reader Env (Reader Env Int -> Reader Env Int)
+lambda1 = return (\yy -> yy >>= \y -> local (insert 'y' (Int y)) expr1)
+
+letx xx body = xx >>= \x -> local (insert 'x' (Int x)) body
+letf ff body = ff >>= \f -> local (insert 'f' (Fun f)) body
+
+-- monadic version of `let x = 2 in let f = \y -> y + x in f 3`
+expr4 :: Reader Env Int
+expr4 = letx (return 2) (letf lambda1 (getf >>= \f -> f (return 3)))
+
+res = runReader expr4 env0 -- will be 5
+