[Haskell-cafe] Re: How do you rewrite your code?
claus.reinke at talk21.com
Thu Mar 4 05:20:14 EST 2010
> All my code, whether neat or not so neat is still way too concrete, too
> 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
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)
A mini demo that shows a few of the implemented refactorings
in action can be found here:
- 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)
More information about the Haskell-Cafe