[Haskell-cafe] Re: How do you rewrite your code?

Claus Reinke 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 
> 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 


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 mailing list