diff --git a/shelly.cabal b/shelly.cabal
--- a/shelly.cabal
+++ b/shelly.cabal
@@ -1,6 +1,6 @@
 Name:       shelly
 
-Version:     1.3.0.5
+Version:     1.3.0.6
 Synopsis:    shell-like (systems) programming in Haskell
 
 Description: Shelly provides convenient systems programming in Haskell,
diff --git a/src/Shelly.hs b/src/Shelly.hs
--- a/src/Shelly.hs
+++ b/src/Shelly.hs
@@ -86,7 +86,7 @@
 import Shelly.Find
 import Control.Monad ( when, unless, void, forM, filterM )
 import Control.Monad.Trans ( MonadIO )
-import Control.Monad.State (get, gets, modify, put)
+import Control.Monad.Reader (ask)
 #if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 706
 import Prelude hiding ( readFile, FilePath, catch)
 #else
@@ -94,6 +94,7 @@
 #endif
 import Data.Char ( isAlphaNum, isSpace )
 import Data.Typeable
+import Data.IORef
 import Data.Sequence (Seq, (|>))
 import Data.Foldable (toList)
 import Data.Maybe
@@ -286,6 +287,11 @@
   trace msg
   action
 
+put :: State -> Sh ()
+put newState = do
+  stateVar <- ask
+  liftIO (writeIORef stateVar newState)
+
 runCommandNoEscape :: [StdHandle] -> State -> FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle)
 runCommandNoEscape handles st exe args = liftIO $ shellyProcess handles st $
     ShellCommand $ T.unpack $ T.intercalate " " (toTextIgnore exe : args)
@@ -374,13 +380,13 @@
 -- | Same as a normal 'catch' but specialized for the Sh monad.
 catch_sh :: (Exception e) => Sh a -> (e -> Sh a) -> Sh a
 catch_sh action handle = do
-    ref <- get
+    ref <- ask
     liftIO $ catch (runSh action ref) (\e -> runSh (handle e) ref)
 
 -- | Same as a normal 'finally' but specialized for the 'Sh' monad.
 finally_sh :: Sh a -> Sh b -> Sh a
 finally_sh action handle = do
-    ref <- get
+    ref <- ask
     liftIO $ finally (runSh action ref) (runSh handle ref)
 
 
@@ -390,7 +396,7 @@
 -- | Same as a normal 'catches', but specialized for the 'Sh' monad.
 catches_sh :: Sh a -> [ShellyHandler a] -> Sh a
 catches_sh action handlers = do
-    ref <- get
+    ref <- ask
     let runner a = runSh a ref
     liftIO $ catches (runner action) $ map (toHandler runner) handlers
   where
@@ -720,10 +726,10 @@
 -- The original state will be restored when the sub-Sh completes.
 -- Exceptions are propagated normally.
 sub :: Sh a -> Sh a
-sub action = do
+sub a = do
   oldState <- get
   modify $ \st -> st { sTrace = T.empty }
-  action `finally_sh` restoreState oldState
+  a `finally_sh` restoreState oldState
   where
     restoreState oldState = do
       newState <- get
@@ -803,6 +809,7 @@
                    , sPathExecutables = Nothing
                    , sErrExit = True
                    }
+  stref <- liftIO $ newIORef def
   let caught =
         action `catches_sh` [
               ShellyHandler (\ex ->
@@ -814,7 +821,7 @@
                                      QuietExit n -> liftIO $ throwIO $ ExitFailure n)
             , ShellyHandler (\(ex::SomeException) -> throwExplainedException ex)
           ]
-  liftIO $ runSh caught def
+  liftIO $ runSh caught stref
   where
     throwExplainedException :: Exception exception => exception -> Sh a
     throwExplainedException ex = get >>= errorMsg >>= liftIO . throwIO . ReThrownException ex
@@ -881,6 +888,8 @@
 -- Commands are in the same form as 'run', but given as pairs
 --
 -- > sshPairs "server-name" [("cd", "dir"), ("rm",["-r","dir2"])]
+--
+-- This interface is crude, but it works for now.
 --
 -- Please note this sets 'escaping' to False: the commands will not be shell escaped.
 -- Internally the list of commands are combined with the string @&&@ before given to ssh.
diff --git a/src/Shelly/Base.hs b/src/Shelly/Base.hs
--- a/src/Shelly/Base.hs
+++ b/src/Shelly/Base.hs
@@ -9,7 +9,7 @@
     Sh, ShIO, unSh, runSh, State(..), StdHandle(..), FilePath, Text,
     relPath, path, absPath, canonic, canonicalize,
     test_d, test_s,
-    trace,
+    unpack, gets, get, modify, trace,
     ls, lsRelAbs,
     toTextIgnore,
     echo, echo_n, echo_err, echo_n_err, inspect, inspect_err,
@@ -38,13 +38,14 @@
 import Filesystem.Path.CurrentOS (FilePath, encodeString, relative)
 import qualified Filesystem.Path.CurrentOS as FP
 import qualified Filesystem as FS
+import Data.IORef (readIORef, modifyIORef, IORef)
 import Data.Monoid (mappend)
 import qualified Data.Text as T
 import qualified Data.Text.IO as TIO
 import Control.Exception (SomeException, catch)
 import Data.Maybe (fromMaybe)
 import Control.Monad.Trans ( MonadIO, liftIO )
-import Control.Monad.State (MonadState, evalStateT, modify, StateT, gets)
+import Control.Monad.Reader (MonadReader, runReaderT, ask, ReaderT)
 import qualified Data.Set as S
 
 -- | ShIO is Deprecated in favor of 'Sh', which is easier to type.
@@ -52,33 +53,27 @@
 {-# DEPRECATED ShIO "Use Sh instead of ShIO" #-}
 
 newtype Sh a = Sh {
-      unSh :: StateT State IO a
-  } deriving (Applicative, Monad, MonadIO, MonadState State, Functor)
+      unSh :: ReaderT (IORef State) IO a
+  } deriving (Applicative, Monad, MonadIO, MonadReader (IORef State), Functor)
 
-runSh :: Sh a -> State -> IO a
-runSh = evalStateT . unSh
+runSh :: Sh a -> IORef State -> IO a
+runSh = runReaderT . unSh
 
-data State = State
-   -- state for the currently ran command
+data State = State 
    { sCode :: Int -- ^ exit code for command that ran
    , sStdin :: Maybe Text -- ^ stdin for the command to be run
    , sStderr :: Text -- ^ stderr for command that ran
-
-   -- state for the envrionment
    , sDirectory :: FilePath -- ^ working directory
-   , sEnvironment :: [(String, String)]
-   , sPathExecutables :: Maybe [(FilePath, S.Set FilePath)] -- ^ cache of executables in the PATH
-
-   -- state for the options
    , sPrintStdout :: Bool   -- ^ print stdout of command that is executed
    , sPrintCommands :: Bool -- ^ print command that is executed
+   , sRun :: [StdHandle] -> State -> FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle) -- ^ command runner, a different runner is used when escaping, probably better to just hold the escaping flag
+   , sEnvironment :: [(String, String)]
+   , sPathExecutables :: Maybe [(FilePath, S.Set FilePath)] -- ^ cache of executables in the PATH
    , sTracing :: Bool -- ^ should we trace command execution
    , sTrace :: Text -- ^ the trace of command execution
    , sErrExit :: Bool -- ^ should we exit immediately on any error
-   , sRun :: [StdHandle] -> State -> FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle) -- ^ command runner, a different runner is used when escaping, probably better to just hold the escaping flag
    }
 
-
 data StdHandle = InHandle StdStream
                | OutHandle StdStream
                | ErrorHandle StdStream
@@ -182,6 +177,22 @@
 test_s = absPath >=> liftIO . \f -> do
   stat <- getSymbolicLinkStatus (encodeString f)
   return $ isSymbolicLink stat
+
+unpack :: FilePath -> String
+unpack = encodeString
+
+gets :: (State -> a) -> Sh a
+gets f = f <$> get
+
+get :: Sh State
+get = do
+  stateVar <- ask 
+  liftIO (readIORef stateVar)
+
+modify :: (State -> State) -> Sh ()
+modify f = do
+  state <- ask 
+  liftIO (modifyIORef state f)
 
 -- | internally log what occurred.
 -- Log will be re-played on failure.
diff --git a/src/Shelly/Find.hs b/src/Shelly/Find.hs
--- a/src/Shelly/Find.hs
+++ b/src/Shelly/Find.hs
@@ -14,7 +14,7 @@
 import Data.Monoid (mappend)
 import System.PosixCompat.Files( getSymbolicLinkStatus, isSymbolicLink )
 import Filesystem (isDirectory)
-import Filesystem.Path.CurrentOS (encodeString)
+import Filesystem.Path.CurrentOS (FilePath, encodeString)
 
 -- | List directory recursively (like the POSIX utility "find").
 -- listing is relative if the path given is relative.
