
All my code, whether neat or not so neat is still way too concrete, too direct. I think the correct answer is one should try to find abstractions and not code straight down to the point. Which to me is still a really tough one, I have to admit.
Taking this cue, since you've raised it before, and because the current thread holds my favourite answer to your problem: "Small moves, Ellie, small moves.." :-) Don't think of "finding abstractions" as an all-or-nothing problem. One good way of finding good abstractions is to start with straight code, to get the functionality out of the way, then try to abstract over repetitive details, improving the code structure without ruining the functionality. The abstractions you're trying to find evolved this way, and can be understood this way, as can new abstractions that have not been found yet. There are, of course, problems one cannot even tackle (in any practical sense) unless one knows some suitable abstractions, and design pattern dictionaries can help to get up to speed there. But unless one has learned to transform straight code into nicer/more concise/more maintainable code in many small steps, using other people's nice abstractions wholesale will remain a "Chinese room" style black art. For instance, the whole point of "refactoring" is to separate general code rewriting into rewrites that change observable behaviour (API or semantics), such as bug fixes, new features, and those that don't change observable behaviour, such as cleaning up, restructuring below the externally visible API, and introducing internal abstractions. Only the latter group fall under refactoring, and turn out to be a nice match to the equational reasoning that pure-functional programmers value so highly. What that means is simply that many small code transformations are thinkable without test coverage (larger code bases should still have tests, as not every typo/thinko is caught by the type system). Even better, complex code transformations, such as large-scale refactorings, can be composed from those small equational-reasoning-based transformations, and these small steps can be applied *without having to understand what the code does* (so they are helpful even for exploratory code reviews: transform/simplify the code until we understand it - if it was wrong before, it will still be wrong the same way, but the issues should be easier to see or fix).
From a glance at this thread, it seems mostly about refactorings/ meaning-preserving program transformations, so it might be helpful to keep the customary distinction between rewriting and refactoring in mind. A couple of lessons we learned in the old "refactoring functional programs" project:
1 refactoring is always with respect to a boundary: things within that boundary can change freely, things beyond need to stay fixed to avoid observable changes. It is important to make the boundary, and the definition of "observable change" explicit for every refactoring session (it might simply mean denotational equivalence, or operational equivalence, or API equivalence, or performance equivalence, or..) 2. refactoring is always with respect to a goal: adding structure, removing structure, changing structure, making code more readable, more maintainable, more concise, .. These goals often conflict, and sometimes even lie in opposite directions (eg.,removing clever abstractions to understand what is going on, or adding clever abstractions to remove boilerplate), so it is important to be clear about the current goal when refactoring. Hth, Claus PS. Obligatory nostalgia: - a long time ago, HaRe did implement some of the refactorings raised in this thread (more were catalogued than implemented, and not all suggestions in this thread were even catalogued, but the project site should still be a useful resource) http://www.cs.kent.ac.uk/projects/refactor-fp/ A mini demo that shows a few of the implemented refactorings in action can be found here: http://www.youtube.com/watch?v=4I7VZV7elnY - once upon a time, a page was started on the haskell wiki, to collect experiences of Haskell code rewriting in practice (the question of how to find/understand advanced design patterns governs both of the examples listed there so far, it would be nice if any new examples raised in this thread would be added to the wiki page) http://www.haskell.org/haskellwiki/Equational_reasoning_examples