diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/bench/Bench.hs b/bench/Bench.hs
--- a/bench/Bench.hs
+++ b/bench/Bench.hs
@@ -2,6 +2,7 @@
 
 module Main (main) where
 
+import qualified Control.Foldl as Foldl
 import           Control.Monad
 import           Criterion.Main
 import qualified Data.Vector as V
@@ -41,6 +42,7 @@
                    , bench "uvectorEnumFromToInt"         $ nfIO $ uvectorEnumFromToInt         0 (1000000 :: Int)    (\_ -> return ())
                 -- Allocates the vector -> linear space and slow
                 -- , bench "uvectorEnumFromNInt"          $ nfIO $ uvectorEnumFromNInt          0 (1000000 :: Int)    (\_ -> return ())
+                   , bench "FoldL.FoldM"                  $ nfIO $ Foldl.foldM (Foldl.FoldM (\_m _x -> return ()) (return ()) return) [1..1000000::Int]
                    ]
 
     , bgroup "w32" [ bench "loop"                          $ nfIO $ loop                        0 (1000000 :: Word32) (\_ -> return ())
@@ -57,6 +59,7 @@
                    , bench "vectorEnumFromToW32"           $ nfIO $ vectorEnumFromToW32         0 (1000000 :: Word32) (\_ -> return ())
                    , bench "uvectorFromListW32"            $ nfIO $ uvectorFromListW32          0 (1000000 :: Word32) (\_ -> return ())
                    , bench "uvectorEnumFromToW32"          $ nfIO $ uvectorEnumFromToW32        0 (1000000 :: Word32) (\_ -> return ())
+                   , bench "FoldL.FoldM"                   $ nfIO $ Foldl.foldM (Foldl.FoldM (\_m _x -> return ()) (return ()) return) [1..1000000::Word32]
                    ]
 
     -- `succ` is almost twice as slow as (+1) because it does an overflow check.
diff --git a/loop.cabal b/loop.cabal
--- a/loop.cabal
+++ b/loop.cabal
@@ -1,5 +1,5 @@
 name:          loop
-version:       0.2.0
+version:       0.3.0
 license:       MIT
 copyright:     2014 Niklas Hambüchen <mail@nh2.me>
 author:        Niklas Hambüchen <mail@nh2.me>
@@ -17,7 +17,7 @@
   `forM_ [1..n]` idiom, which in many cases GHC cannot fuse to efficient
   code.
   .
-  See https://ghc.haskell.org/trac/ghc/ticket/8763.
+  See <https://ghc.haskell.org/trac/ghc/ticket/8763>.
 
 source-repository head
   type:      git
@@ -64,6 +64,7 @@
     , criterion           >= 0.6.0.0
     , random              >= 1.0.1.1
     , vector              >= 0.10.9.1
+    , foldl               >= 1.0.0
   ghc-options:
     -Wall -O2
 
diff --git a/src/Control/Loop.hs b/src/Control/Loop.hs
--- a/src/Control/Loop.hs
+++ b/src/Control/Loop.hs
@@ -26,8 +26,10 @@
 --   @forLoop 1 (<= n) (+1)@.
 module Control.Loop
   ( forLoop
+  , forLoopState
   , forLoopFold
   , numLoop
+  , numLoopState
   , numLoopFold
   ) where
 
@@ -43,6 +45,17 @@
 {-# INLINE forLoop #-}
 
 
+-- | @forLoopState start cond inc initialState f@: A C-style for loop with
+-- starting value, loop condition, incrementor and a state that is threaded
+-- through the computation.
+forLoopState :: (Monad m) => a -> (a -> Bool) -> (a -> a) -> b -> (b -> a -> m b) -> m b
+forLoopState start cond inc initialState f = go start initialState
+  where
+    go !x !state | cond x    = f state x >>= go (inc x)
+                 | otherwise = return state
+
+{-# INLINE forLoopState #-}
+
 -- | @forLoopFold start cond inc acc0 f@: A pure fold using a for loop
 -- instead of a list for performance.
 --
@@ -62,15 +75,29 @@
 -- | @numLoop start end f@: Loops over a contiguous numerical range, including
 -- @end@.
 --
+-- Does nothing when not @start <= end@.
+--
 -- It uses @(+ 1)@ so for most integer types it has no bounds (overflow) check.
-numLoop :: (Num a, Eq a, Monad m) => a -> a -> (a -> m ()) -> m ()
-numLoop start end f = go start
+numLoop :: (Num a, Ord a, Monad m) => a -> a -> (a -> m ()) -> m ()
+numLoop start end f = if start <= end then go start else return ()
   where
     go !x | x == end  = f x
           | otherwise = f x >> go (x+1)
 
 {-# INLINE numLoop #-}
 
+-- | @numLoopState start end f initialState@: Loops over a contiguous numerical
+-- range, including @end@ threading a state through the computation.
+--
+-- It uses @(+ 1)@ so for most integer types it has no bounds (overflow) check.
+numLoopState :: (Num a, Eq a, Monad m) => a -> a -> b -> (b -> a -> m b) -> m b
+numLoopState start end initState f = go start initState
+  where
+    go !x !state | x == end  = f state x
+                 | otherwise = f state x >>= go (x+1)
+
+
+{-# INLINE numLoopState #-}
 
 -- | @numLoopFold start end acc0 f@: A pure fold over a contiguous numerical
 -- range, including @end@.
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -5,7 +5,7 @@
 import           Data.Word
 import           Test.Hspec
 
-import           Control.Loop (forLoop, numLoop, numLoopFold)
+import           Control.Loop
 
 
 main :: IO ()
@@ -53,6 +53,18 @@
       res `shouldBe` (maxBound :: Word32)
 
 
+  describe "forLoopState" $ do
+
+    it "monadic and threaded state" $ do
+      let res = flip execState 0 $ do
+            _ <- forLoopState (0 :: Int) (<= 10) (+1) (1 :: Int) $ \st i -> do
+                    x <- get
+                    put $! x + st + i
+                    return (st * 2)
+            return ()
+
+      res `shouldBe` sum (map (\a -> a + 2 ^ a) [0 .. 10])
+
   describe "numLoop" $ do
 
     it "is inclusive" $ do
@@ -62,6 +74,19 @@
               put $! x + i
 
       res `shouldBe` 55
+
+
+  describe "numLoopState" $ do
+
+    it "monadic and threaded state" $ do
+      let res = flip execState 0 $ do
+            _ <- numLoopState (0 :: Int) 10 (1 :: Int) $ \st i -> do
+                        x <- get
+                        put $! x + st + i
+                        return (st * 2)
+            return ()
+
+      res `shouldBe` sum (map (\a -> a + 2 ^ a) [0 .. 10])
 
 
   describe "numLoopFold" $ do
