diff --git a/examples/basic/Main.hs b/examples/basic/Main.hs
new file mode 100644
--- /dev/null
+++ b/examples/basic/Main.hs
@@ -0,0 +1,15 @@
+module Main (main) where
+
+import Control.Monad (forM_)
+import System.Directory.PathWalk (pathWalk)
+import System.Environment (getArgs)
+
+main :: IO ()
+main = do
+  rawArgs <- getArgs
+  let args = if rawArgs == [] then ["."] else rawArgs
+  forM_ args $ \arg -> do
+    pathWalk arg $ \root dirs files -> do
+      putStrLn root
+      putStrLn $ "  dirs: " ++ show dirs
+      putStrLn $ "  files: " ++ show files
diff --git a/examples/example/Main.hs b/examples/example/Main.hs
deleted file mode 100644
--- a/examples/example/Main.hs
+++ /dev/null
@@ -1,15 +0,0 @@
-module Main (main) where
-
-import Control.Monad (forM_)
-import System.Directory.PathWalk (pathWalk)
-import System.Environment (getArgs)
-
-main :: IO ()
-main = do
-  rawArgs <- getArgs
-  let args = if rawArgs == [] then ["."] else rawArgs
-  forM_ args $ \arg -> do
-    pathWalk arg $ \root dirs files -> do
-      putStrLn root
-      putStrLn $ "  dirs: " ++ show dirs
-      putStrLn $ "  files: " ++ show files
diff --git a/examples/lazy/Main.hs b/examples/lazy/Main.hs
new file mode 100644
--- /dev/null
+++ b/examples/lazy/Main.hs
@@ -0,0 +1,16 @@
+module Main (main) where
+
+import Control.Monad (forM_)
+import System.Directory.PathWalk (pathWalkLazy)
+import System.Environment (getArgs)
+
+main :: IO ()
+main = do
+  rawArgs <- getArgs
+  let args = if rawArgs == [] then ["."] else rawArgs
+  forM_ args $ \arg -> do
+    results <- pathWalkLazy arg
+    forM_ results $ \(root, dirs, files) -> do
+      putStrLn root
+      putStrLn $ "  dirs: " ++ show dirs
+      putStrLn $ "  files: " ++ show files
diff --git a/pathwalk.cabal b/pathwalk.cabal
--- a/pathwalk.cabal
+++ b/pathwalk.cabal
@@ -1,5 +1,5 @@
 name:                pathwalk
-version:             0.2.1.0
+version:             0.3.0.0
 synopsis:            Path walking utilities for Haskell programs
 description:         Simple directory tree walking utilities.
 license:             MIT
@@ -44,16 +44,26 @@
   default-language: Haskell2010
   ghc-options: -Wall
 
-executable example
+test-suite basic
+  type: exitcode-stdio-1.0
   main-is: Main.hs
   build-depends: base, pathwalk
-  hs-source-dirs: examples/example
+  hs-source-dirs: examples/basic
   default-language: Haskell2010
   ghc-options: -Wall
 
-executable stoprecursing
+test-suite stoprecursing
+  type: exitcode-stdio-1.0
   main-is: Main.hs
   build-depends: base, pathwalk
   hs-source-dirs: examples/stoprecursing
+  default-language: Haskell2010
+  ghc-options: -Wall
+
+test-suite lazy
+  type: exitcode-stdio-1.0
+  main-is: Main.hs
+  build-depends: base, pathwalk
+  hs-source-dirs: examples/lazy
   default-language: Haskell2010
   ghc-options: -Wall
diff --git a/src/System/Directory/PathWalk.hs b/src/System/Directory/PathWalk.hs
--- a/src/System/Directory/PathWalk.hs
+++ b/src/System/Directory/PathWalk.hs
@@ -5,18 +5,21 @@
     , pathWalk
     , WalkStatus(..)
     , pathWalkInterruptible
+    , pathWalkLazy
     ) where
 
-import Control.Monad (forM_, filterM)
+import Control.Monad (forM, forM_, filterM)
+import Control.Monad.IO.Class (MonadIO, liftIO)
 import System.Directory (doesDirectoryExist, doesFileExist, getDirectoryContents)
 import System.FilePath ((</>))
 import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)
+import System.IO.Unsafe (unsafeInterleaveIO)
 
 -- | Called with a directory, list of relative subdirectories, and a
 -- list of file names.  If using 'pathWalk', the callback always
 -- returns '()'.  If using 'pathWalkInterruptible', it returns whether
 -- to continue, prevent recursing further, or stop traversal entirely.
-type Callback a = FilePath -> [FilePath] -> [FilePath] -> IO a
+type Callback m a = FilePath -> [FilePath] -> [FilePath] -> m a
 
 -- | 'pathWalk' recursively enumerates the given root directory,
 -- calling callback once per directory with the traversed directory
@@ -31,7 +34,7 @@
 --     when ("Test.hs" \`isSuffixOf\` file) $ do
 --       registerTestFile $ dir \</\> file
 -- @
-pathWalk :: FilePath -> Callback () -> IO ()
+pathWalk :: MonadIO m => FilePath -> Callback m () -> m ()
 pathWalk root callback = do
   pathWalkInterruptible root $ \dir dirs files -> do
     callback dir dirs files
@@ -45,14 +48,20 @@
   | Stop -- ^ Stop recursing entirely.
   deriving (Show, Eq)
 
-pathWalkInternal :: FilePath -> Callback WalkStatus -> IO (Maybe ())
-pathWalkInternal root callback = do
+readDirsAndFiles :: FilePath -> IO ([FilePath], [FilePath])
+readDirsAndFiles root = do
   names <- getDirectoryContents root
   let properNames = filter (`notElem` [".", ".."]) names
 
   dirs <- filterM (\n -> doesDirectoryExist $ root </> n) properNames
   files <- filterM (\n -> doesFileExist $ root </> n) properNames
+  
+  return (dirs, files)
 
+pathWalkInternal :: MonadIO m => FilePath -> Callback m WalkStatus -> m (Maybe ())
+pathWalkInternal root callback = do
+  (dirs, files) <- liftIO $ readDirsAndFiles root
+
   result <- callback root dirs files
   case result of
     Continue -> do
@@ -67,7 +76,25 @@
 -- | Traverses a directory tree, just like 'pathWalk', except that
 -- the callback can determine whether to continue traversal.  See
 -- 'WalkStatus'.
-pathWalkInterruptible :: FilePath -> Callback WalkStatus -> IO ()
+pathWalkInterruptible :: MonadIO m => FilePath -> Callback m WalkStatus -> m ()
 pathWalkInterruptible root callback = do
   _ <- pathWalkInternal root callback
   return ()
+
+-- | The lazy version of 'pathWalk'.  Instead of running a callback
+-- per directory, it returns a lazy list that reads from the
+-- filesystem as the list is evaluated.
+--
+-- 'pathWalkLazy' does not allow selective recursion.  For richer
+-- functionality, see the directory-tree package at
+-- https://hackage.haskell.org/package/directory-tree
+pathWalkLazy :: MonadIO m => FilePath -> m [(FilePath, [FilePath], [FilePath])]
+pathWalkLazy root = liftIO $ unsafeInterleaveIO $ do
+  (dirs, files) <- readDirsAndFiles root
+
+  next <- unsafeInterleaveIO $ do
+    allsubs <- forM dirs $ \dir -> do
+      pathWalkLazy $ root </> dir
+    return $ concat allsubs
+    
+  return $ (root, dirs, files) : next
