"Log in", extract number from a map, increment it, and log out
I would inline, eta-reduce and use more library functions to make the code short enough that adding another command is trivial. Perhaps give the fields names so adding another can't mess up numbering, and you need less comments because the code describes itself.

Edit: I'll use non, making the map not add a value for new users until they want to change it. This way we don't need to initialize with db . at user %= (&lt;|&gt; Just 0) on logging in, and we can get rid of the Just returned by the "get" action without needing to promise we already initialized. (Which we don't necessarily have, as the initial ""!)

{#- LANGUAGE TemplateHaskell, LambdaCase -#}
import Control.Lens -- batteries included
import Control.Monad.Trans.Maybe
import Control.Applicative -- (&lt;$&gt;), (&lt;*&gt;), empty, (&lt;|&gt;)
import qualified Data.Map as M -- Data.Map's name collisions with prelude are customarily handled by qualification

data AppState = AppState
  { _appStateUsername :: String
  , _appStateDb :: M.Map String Int
  }

makeFields ''AppState

-- Here's one we can actually outline, because it's used more than once, and it allows us to pull out the liftIO, and it shows us that we forgot the hFlush and the trailing ' ' one of the times.
prompt :: MonadIO m =&gt; String -&gt; m String
prompt s = liftIO $ do
  putStr $ s ++ ": "
  hFlush stdout
  getLine

main = runMaybeT $ (`execStateT` AppState "" M.empty) $ forever $ do
  prompt "next action" &gt;&gt;= \case
    "inc" -&gt; do
      user &lt;- use username
      db . at user . non 0 += 1
    "login" -&gt; do
      user &lt;- prompt "enter username"
      username .= user
    "get" -&gt; do
      n &lt;- M.findWithDefault 0 &lt;$&gt; use username &lt;*&gt; use db
      liftIO $ putStrLn $ "current num: " ++ show n -- Note that this includes the Maybe, even though at this point we're always just, unless we're still the initial "".
    "logout" -&gt; do
      username .= "" -- Note that this makes for shenanigans if someone logs in as "".
    "stop" -&gt; empty -- MaybeT helps us out with the control flow to get rid of the special case and recursion.
    _ -&gt; liftIO $ putStrLn $ "usage: ehhh just look at the code mmkay"