Problem: Over time, code becomes obscured by initialization and cleanup, special cases, and other things.
A solution: Alternate in the program's control flow between primary and details functions. So each "primary function" containing what matters to someone understanding what the code does, is called by a "details function." The details functions take the cruft.
An example, a program to recursively index directories. It started out clear, like this:
indexDirectory addsRef dir = do contents <- getDirectoryContents dir mapM_ (\nm -> let name = dir ++ nm in unless (nm == "." || nm == "..") $ do b <- doesFileExist name if b then index addsRef name else do indexDirectory addsRef (name ++ [pathDelimiter])) contentsBut it grew over time to be like this:
indexDirectory dir logicalDir = do contents <- getDirectoryContents dir mapM_ (\nm -> let name = dir ++ nm in let logicalName = logicalDir ++ nm in unless (nm == "." || nm == "..") $ do b <- doesFileExist name if b then maybe (index name logicalName) (\f -> do unpacked <- f name indexDirectory unpacked (logicalName ++ "@")) (lookup (takeExtension name) unpacks) else do userdata <- getAppUserDataDirectory "Index" tmp <- getTemporaryDirectory unless (name == userdata || name == tmp || name == "/dev" || name == "/sys") (indexDirectory (name ++ [pathDelimiter]) (logicalName ++ [pathDelimiter]))) contentsBut then by abstracting the details, I got this, which is similar to the original:
indexDirectory dir logicalDir = do contents <- getDirectoryContents dir mapM_ (\nm -> let name = dir ++ nm in let logicalName = logicalDir ++ nm in unless (nm == "." || nm == "..") $ do b <- doesFileExist name if b then details1 name logicalName {-Primary control flow:-}(index name logicalName) else details2 name {-Primary control flow:-}(indexDirectory (name ++ [pathDelimiter]) (logicalName ++ [pathDelimiter]))) contents