Keep reading user keyboard input if available, while echoing back the latest available
With a bit of suggestion/help on Haskell's IRC channels, I came up with this, which doesn't forkIO at every recursion.
{-# language LambdaCase #-}

import Control.Concurrent
import Data.Maybe
import System.IO

main :: IO ()
main = do
  hSetBuffering stdin NoBuffering
  future_input &lt;- newEmptyMVar
  forkIO $ pollInput future_input
  processInput future_input 'l'

pollInput :: MVar Char -&gt; IO ()
pollInput future_input = (putMVar future_input =&lt;&lt; getChar) &gt;&gt; pollInput future_input

processInput :: MVar Char -&gt; Char -&gt; IO ()
processInput future_input latest_input = tryTakeMVar future_input &gt;&gt;=
    \case Just input -&gt; putStrLn (&quot;direction change &quot; ++ return input)
                        &gt;&gt; processInput future_input input
          Nothing    -&gt; putStrLn (&quot;keep direction &quot; ++ return latest_input)
                        &gt;&gt; threadDelay 100000
                        &gt;&gt; processInput future_input latest_input