Hm... for anything real, you would need to provide good error reports, i.e. why did it fail to parse. Code like this looks pretty, but once you add in good error reports, I feel like it tends to look almost exactly the same as in an imperative language?
For a real use case, consider using a parser library. If you ignore the boilerplate import stuff, it's IMHO rather short and elegant - and not very imperative:
{-# LANGUAGE OverloadedStrings #-}
import Data.Text (Text)
import Data.Void (Void)
import qualified Data.Text as T
import Text.Megaparsec
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L
type Parser = Parsec Void Text
numParser :: Parser [Integer]
numParser = L.decimal `sepBy` space1
-- just for demonstration purposes - this will print an ugly debug string, it can be customised
main = putStrLn $ show $ parse numParser "some-source-file.txt" "10 20 30 x"
-- this would print "Right [10, 20, 30]"
-- main = putStrLn $ show $ parse numParser "some-source-file.txt" "10 20 30"