The Morra game implementation in Haskell
You don't really use the StateT to your advantage. Your play function can be written as

play :: Int -&gt; ([String], Int) -&gt; IO ([String], Int)
play p (ps, n) = do
  let currp = ps !! p
  if length ps == 1
    then return (ps, n)
    else do
      putStr $ "It's now " ++ currp ++ "'s turn. How many fingers will be help up? "
      a &lt;- safeGetLine
      putNewLine
      putStrLn "[INPUT PHASE] Each player now chooses, how many fingers does (s)he hold up."
      fs &lt;- getFingers (length ps)
      putStr "[END OF INPUT PHASE]"
      putNewLine
      if sum fs == a
        then do
          putStrLn $ currp ++ " has guessed right, he's out."
          putNewLine
          let newps = delete currp ps
          play ((p + 1) `mod` length newps) (newps, n + 1)
        else do
          putStrLn $ currp ++ " hasn't guessed right, the game goes on!"
          putNewLine
          play ((p + 1) `mod` length ps) (ps, n + 1)
  where
    putNewLine = putStrLn ""


That's not really surprising, since State s a is more or less just an abstraction of s -&gt; (s, a). Since a is () in your case, we can just get rid of it and end up with s -&gt; IO s. We can use State(T) to our advantage if we have several Stateful functions. However, we only have play.

Speaking about play, there are several nitpicks:


You use length several times, although a single length call would suffice.
You should use guess instead of a as name.


If we apply both, we end up with

play :: Int -&gt; StateT ([String], Int) IO ()
play p = do
  (ps, n) &lt;- get
  let currp = ps !! p
  let plength = length ps
  unless (plength == 1) $ do
      liftIO $ putStr $ "It's now " ++ currp ++ "'s turn. How many fingers will be help up? "
      guess &lt;- liftIO safeGetLine
      putNewLine
      liftIO $ putStrLn "[INPUT PHASE] Each player now chooses, how many fingers does (s)he hold up."
      fs &lt;- liftIO $ getFingers plength
      liftIO $ putStr "[END OF INPUT PHASE]"
      putNewLine
      if sum fs == guess
        then do
          liftIO $ putStrLn $ currp ++ " has guessed right, he's out."
          putNewLine
          put (delete currp ps, n + 1)
          play ((p + 1) `mod` (plength - 1))
        else do
          liftIO $ putStrLn $ currp ++ " hasn't guessed right, the game goes on!"
          putNewLine
          put (ps, n + 1)
          play ((p + 1) `mod` plength)
  where
    putNewLine = liftIO $ putStrLn ""


We didn't  gain that much, did we? So let's split play:

type Player     = String
type Turn       = Int
type MorraT m a = StateT ([Player], Turn) m a
type Morra a    = MorraT IO a

play :: Morra ()
play = do
   pcount &lt;- getPlayerCount
   unless (pcount == 1) $ do
     currp &lt;- getNextPlayer
     guess &lt;- guessFingers currp
     fingers &lt;- getFingers
     if fingers == guess 
       then do
         putStrLn' $ currp ++ " has guessed right, they're out!"
         removePlayer currp
       else do
         putStrLn' $ currp ++ " hasn't guessed right, the game goes on!"
         increaseTurnCount
         play
  where
    putStrLn' xs = liftIO $ putStrLn xs &gt;&gt; putStrLn ""


That's easy enough to undestand. We also added some types so that we can change our program in a single line, for example if you want to change the StateT to something else later.

Now we need our helper functions:

players :: Monad m =&gt; MorraT m [Player]
players = fst &lt;$&gt; get


That's a convenient small function and makes it possible to change our Morra later without changing too many functions.

For our getNextPlayer we want to get the next player as well as queue them to the back of the line. A list isn't the perfect data structure here, by the way, but we keep it for simplicity:

getNextPlayer :: Monad m =&gt; MorraT m Player
getNextPlayer = do
  (p:ps) &lt;- players
  put (ps ++ [p])
  return p


guessFingers looks exactly how you'd imagine it. It doesn't need to be in Morra, as it does not inspect the state, but let's keep it there for simplicity, again:

guessFingers :: Player -&gt; Morra Int
guessFingers p = do
  liftIO $ putStrLn $ p ++ ", what's your guess?"
  liftIO $ safeGetLine


Your variant of getFingers only tells a person to give the PC to the next player, but it does not tell them who the next player is going to be. That can be fixed by using forM instead of replicateM:

getFingers :: Morra Int
getFingers = do
  ps &lt;- players
  fmap sum $ forM ps $ \p -&gt;
    liftIO $ putStrLn $ "How many fingers do you hold up, " ++ p ++ "?"
    liftIO $ getSafeLine


removePlayer looks the same as yours:

removePlayer :: Monad m =&gt; Player -&gt; MorraT m ()
removePlayer p = modify $ \(ps, n) -&gt; (delete p ps, n)


Note that a setPlayers or withPlayers function would make both this and getNextPlayer simpler.

increaseTurnCount :: Monad m =&gt; MorraT m ()
increaseTurnCount = modify $ \(ps, n) -&gt; (ps, n + 1)


That's it. We now have several functions that can be used independently but act on the same state. By the way, the single increaseTurnCount hints that the amount of Turns isn't really part of the State, but could be used as the return value of play instead.

For completeness, we can provide

runMorra :: Monad m =&gt; [Player] -&gt; MorraM m a -&gt; m (Player, Int)
runMorra ps = do
  ([p], n) &lt;- execStateT play (ps, 0)
  return (p, n)


One last remark on safeGetLine. Instead of all isNumber use readMaybe from Text.Read:

safeGetLine :: IO Int
safeGetLine = do
  x &lt;- getLine
  case readMaybe x of
    Just n  -&gt; return n
    Nothing -&gt; putStrLn "Please input a number" &gt;&gt; safeGetLine 


Otherwise your safeGetLine isn't safe, since isNumber also returns True on Unicode Characters in the 'Number, Other' Category.