diff --git a/app/Main.hs b/app/Main.hs
index fd999a1a2adaea68a2b5efb5424e7577e5736f51..615aabe7cf0c90bb1eb0fbbc1fc798809033179f 100644
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -2,6 +2,8 @@ module Main where
 
 import Data.Version
 
+import Control.Monad
+import Data.Bifunctor
 import Data.Semigroup
 import Options.Applicative hiding (ParseError)
 import System.Console.Shell
@@ -40,32 +42,26 @@ data Globals n t
   = GlobalsUntyped (Map.Map n (Lambda.LambdaExpr n))
   | GlobalsSystemF (Map.Map n (SystemF.SystemFExpr n t))
 
+newGlobals :: Language -> Globals n t
+newGlobals Untyped = GlobalsUntyped Map.empty
+newGlobals SystemF = GlobalsSystemF Map.empty
+
 -- | The result of an evaluation
 type Result a state = Either a           -- ^ An error
                              (a, state)  -- ^ The result along with its state
 
 -- | Represent a language together with its evaluation function
-data Eval a = Eval a (String -> Result String (Globals String String))
+data Eval a = Eval a (Globals String String -> String -> Result String (Globals String String))
 
 untyped :: Eval Language 
 untyped = Eval Untyped eval
-  where eval = fromEvalString Lambda.evalString
+  where eval (GlobalsUntyped g) s = bimap show toResult $ Lambda.evalString g s
+        toResult (e, g) = (prettyPrint e, GlobalsUntyped g)
 
 systemf :: Eval Language
 systemf = Eval SystemF eval
-  where eval = fromEvalString SystemF.evalString
-
--- | Take a typed evaluation function and return a function that returns a result
--- 
---   For example:
---     (String -> Either ParseError (LambdaExpr String)) -> (String -> Result String)
---     (String -> Either ParseError (SystemFExpr String String)) -> (String -> Result String)
-fromEvalString :: (Show s, PrettyPrint p)
-               => (String -> Either s p)
-               -> (String -> Result String (Globals String String))
-fromEvalString f = either (Left . show) (Right . toResult) . f
-  -- TODO[sgillespie]: Remove placeholder below
-  where toResult expr = (prettyPrint expr, GlobalsUntyped Map.empty)
+  where eval (GlobalsSystemF g) s = bimap show toResult $ SystemF.evalString g s
+        toResult (e, g) = (prettyPrint e, GlobalsSystemF g)
 
 cliParser :: Parser CliOptions
 cliParser = CliOptions 
@@ -82,11 +78,11 @@ cliParser = CliOptions
 runShell' :: CliOptions -> IO ()
 runShell' CliOptions{version=True} = putStrLn version'
 runShell' CliOptions{language=Eval lang eval} 
-  = runShell (mkShellDesc lang eval) haskelineBackend ()
+  = void $ runShell (mkShellDesc lang eval) haskelineBackend (newGlobals lang)
 
 mkShellDesc :: Language 
-            -> (String -> Result String (Globals String String))
-            -> ShellDescription ()
+            -> (Globals String String -> String -> Result String (Globals String String))
+            -> ShellDescription (Globals String String)
 mkShellDesc language f = shellDesc' $ mkShellDescription commands (eval f)
   where shellDesc' d = d {
           greetingText = Just shellGreeting,
@@ -107,10 +103,18 @@ commands = [
   helpCommand "h"
   ]
 
-eval :: (String -> Result String (Globals String String)) -> String -> Sh s' ()
-eval f = either shellPutErrLn shellPutStrLn . fmap fst . f
+eval :: (Globals String String -> String -> Result String (Globals String String))
+     -> String
+     -> Sh (Globals String String) ()
+eval f str = do
+  globals <- getShellSt
+
+  case f globals str of
+    Left err -> shellPutErrLn err
+    Right (result, globals') -> do
+      putShellSt globals'
+      shellPutStr result
 
 -- | Get the current version
 version' :: String
 version' = showVersion P.version
- 
diff --git a/src/Language/Lambda.hs b/src/Language/Lambda.hs
index 2b58dd336f544c3012ed9c0ef63f3d72f6b0315e..3ba7d4d51713d0219c817b0338f7f84469c14eca 100644
--- a/src/Language/Lambda.hs
+++ b/src/Language/Lambda.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE FlexibleInstances #-}
 module Language.Lambda (
+  Globals(..),
   LambdaExpr(..),
   ParseError(..),
   PrettyPrint(..),
@@ -12,13 +13,20 @@ module Language.Lambda (
 import Control.Monad
 import Text.Parsec
 
+import qualified Data.Map as Map
+
 import Language.Lambda.Eval
 import Language.Lambda.Expression
 import Language.Lambda.Parser
 import Language.Lambda.Util.PrettyPrint
 
-evalString :: String -> Either ParseError (LambdaExpr String)
-evalString = fmap (evalExpr uniques) . parseExpr
+type Globals = Map.Map String (LambdaExpr String)
+
+evalString :: Globals
+           -> String
+           -> Either ParseError (LambdaExpr String, Globals)
+evalString globals str = flip (,) globals <$> eval'
+  where eval' = evalExpr uniques <$> parseExpr str
 
 uniques :: [String]
 uniques = concatMap (\p -> map (:p) . reverse $ ['a'..'z']) suffix
diff --git a/src/Language/SystemF.hs b/src/Language/SystemF.hs
index 422ef1428b40e7d6b56d21c86740a17376c8c6f4..634f354e1ef36001ee9eae2bfbca8d30fadd843c 100644
--- a/src/Language/SystemF.hs
+++ b/src/Language/SystemF.hs
@@ -1,4 +1,5 @@
 module Language.SystemF (
+  Globals(..),
   PrettyPrint(..),
   SystemFExpr(..),
   evalString,
@@ -7,10 +8,16 @@ module Language.SystemF (
 
 import Text.Parsec
 
+import qualified Data.Map as Map
+
 import Language.Lambda.Util.PrettyPrint
 import Language.SystemF.Expression
 import Language.SystemF.Parser
 
-evalString :: String -> Either ParseError (SystemFExpr String String)
-evalString = parseExpr
+type Globals = Map.Map String (SystemFExpr String String)
+
+evalString :: Globals
+           -> String
+           -> Either ParseError (SystemFExpr String String, Globals)
+evalString globals = fmap (flip (,) globals) . parseExpr
 
diff --git a/test/Language/Lambda/HspecUtils.hs b/test/Language/Lambda/HspecUtils.hs
index dd416384a1a519c7620b0005f01d698aebe31852..c9927ef2e5f3123cbe39d0e10573f7209941ad4b 100644
--- a/test/Language/Lambda/HspecUtils.hs
+++ b/test/Language/Lambda/HspecUtils.hs
@@ -1,5 +1,6 @@
 module Language.Lambda.HspecUtils where
 
+import Data.Map (empty)
 import Test.Hspec
 
 import Language.Lambda
@@ -8,4 +9,4 @@ shouldEvalTo :: String -> String -> Expectation
 shouldEvalTo s1 = shouldBe (eval s1) . eval
 
 eval :: String -> Either ParseError (LambdaExpr String)
-eval = evalString
+eval = fmap fst . evalString empty
diff --git a/test/Language/LambdaSpec.hs b/test/Language/LambdaSpec.hs
index 44cc67c3532f10f694a05ac5cfd84e3c2adb4887..13265e909cab1445bc6e37c4ecd242fe31e0abd2 100644
--- a/test/Language/LambdaSpec.hs
+++ b/test/Language/LambdaSpec.hs
@@ -3,20 +3,21 @@ module Language.LambdaSpec where
 import Test.Hspec
 
 import Language.Lambda
+import Language.Lambda.HspecUtils
 
 spec :: Spec
 spec = do
   describe "evalString" $ do
     it "evaluates simple strings" $ do
-      evalString "x" `shouldBe` Right (Var "x")
-      evalString "\\x. x" `shouldBe` Right (Abs "x" (Var "x"))
-      evalString "f y" `shouldBe` Right (App (Var "f") (Var "y"))
+      eval "x" `shouldBe` Right (Var "x")
+      eval "\\x. x" `shouldBe` Right (Abs "x" (Var "x"))
+      eval "f y" `shouldBe` Right (App (Var "f") (Var "y"))
 
     it "reduces simple applications" $
-      evalString "(\\x .x) y" `shouldBe` Right (Var "y")
+      eval "(\\x .x) y" `shouldBe` Right (Var "y")
 
     it "reduces applications with nested redexes" $
-      evalString "(\\f x. f x) (\\y. y)" `shouldBe` Right (Abs "x" (Var "x"))
+      eval "(\\f x. f x) (\\y. y)" `shouldBe` Right (Abs "x" (Var "x"))
 
   describe "uniques" $ do
     let alphabet = reverse ['a'..'z']