DetailsPattern

Last edit October 9, 2013
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])) contents

But 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])))
		contents

But 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