> imperative code with some local mutability
Haskell allows this, you just have to actually demonstrate that the mutation is local:
statefulSum :: [Int] -> Int
statefulSum xs = runST $ do
n <- newSTRef 0
traverse (\x -> modifySTRef n (\y -> y + x)) xs
readSTRef n