in reply to Re: FP languages have a bottom-up bias?
in thread TMTOWTDI... and most of them are wrong
This is legal Haskell that compiles and executes. It doesn't do much, however, merely echoing its input:module Main where main = interact $ showOutput . process . readInput showOutput = id process = id readInput = id
$ echo alpha beta | ghc -e main WordIndex.hs alpha betaStill, it shows how easy it is to start sketching a solution at a high level.
Next, working down, I will flesh out the outline. I won't worry about reading input from files or writing output to an index just yet. For now, I will focus on the core word-counting problem, adding logic to do just that:
Notice that I did not need to alter the outline. All I did was add low-level detail to it. Now I have counts:module Main where import qualified Data.Map as Map main = interact $ showOutput . process . readInput showOutput = unlines . Map.foldWithKey show1  process = foldl accumulate Map.empty readInput = words show1 key count = (unwords [key, show count] :) accumulate hash key = Map.insertWith (+) key 1 hash
$ echo alpha beta | ghc -e main WordIndex.hs alpha 1 beta 1
Continuing to refine the lower-level functionality, I will return to the I/O tasks. Like before, I will add lower-level functions, leaving the top-level outline largely unchanged:
module Main (main) where import Control.Monad import qualified Data.Map as Map import System.Environment main = control $ showOutput . process . readInput showOutput = unlines . Map.foldWithKey show1  process = foldl accumulate Map.empty readInput = words show1 key count = (unwords [key, show count] :) accumulate hash key = Map.insertWith (+) key 1 hash control f = readFiles >>= writeIndex . f readFiles = getArgs >>= liftM concat . mapM readFile writeIndex = writeFile "words.index"
Let me recap. I started with a top-level, executable outline and worked down from there. With two simple iterations I was able to add the counting functionality and then the I/O functionality. Now I have a complete solution that is largely equivalent to your code (ignoring details that don't matter for our top-down coding discussion).
As a demonstration, I will compile the code and use it to index itself:
$ ghc -O2 --make -o WordIndex WordIndex.hs $ ./WordIndex *.hs $ cat words.index "words.index" 1 $ 1 (+) 1 (main) 1 (unwords 1 . 5 1 1 :) 1 = 9 >>= 2 Control.Monad 1 Data.Map 1 Main 1 Map 1 Map.empty 1 ...
There you have it: top-down functional programming, or at least the way I do it in Haskell. I hope you will trust me that it was quick and easy. (It took much longer to write this explanation than the code.)