-module (sample).-export([printList/1]).printList(L) ->printList(fun(H) ->io:format("~p\n", [H]) end, L).printList(F,[H|T]) ->F(H),printList(F, T);printList(F,[]) -> true.
Figure 3.
The program after generalisation
-module (sample).-export([printList/1]).printList(L) ->forEach(fun(H) ->io:format("~p\n", [H]) end, L).forEach(F,[H|T]) ->F(H),forEach(F, T);forEach(F,[]) -> true.
Figure 4.
The program after renamingThe example presented here is small-scale, but it ischosen to illustrate aspects of refactoring which canscale to larger programs and multi-module systems.In Figure1,the function
printList/1
has beendefined to print all elements of a list to the standardoutput. Next, suppose the user would like to defineanother function,
broadcast/1
, which broadcasts amessage to a list of processes.
broadcast/1
has avery similar structure to
printList/1
, as they bothiterate over a list doing something to each element inthe list. Na¨ıvely, the new function could be added bycopy, paste, and modification as shown in Figure2.However, a
refactor then modify
strategy, as shown inFigures3-5, would make the resulting code easier to
maintain and reuse.Figure3shows the result of generalising the func-tion
printList
on the sub-expression
io:format("~p/n",~[H])
The expression contains the variable
H
, which is onlyin scope within the body of
printList
. Instead of generalising over the expression itself, the transforma-
-module (sample).-export([printList/1, broadcast/1]).printList(L) ->forEach(fun(H) ->io:format("~p\n", [H]) end, L).broadcast(Pids)->forEach(fun(H) ->H ! "The message" end, Pids).forEach(F,[H|T]) ->F(H),forEach(F, T);forEach(F,[]) -> true.
Figure 5.
The program after adding a functiontion is achieved by first abstracting over the free vari-able
H
, and by making the generalised parameter a
function
F
. In the body of
printList
the expression
io:format("~p/n", H])
has been replaced with
F
applied to the local variable
H
.The arity of the
printList
has thus changed; in or-der to preserve the interface of the module, we create anewfunction,
printList/1
,asanapplicationinstanceof
printList/2
with the first parameter supplied withthe function expression:
fun(H) -> io:format("~p/n", [H]) end
.Note that this transformation gives
printList
a func-tional argument, thus making it a characteristically‘functional’ refactoring.Figure4shows the result of renaming
printList/2
to
forEach/2
. The new function name reflects thefunctionality of the function more precisely. In Figure5,function
braodcast/1
is added as another applica-tion instance of
forEach/2
.Refactorings to generalise a function definition andto rename an identifier are typical structural refactor-ings, implemented in our work on both Haskell and Er-lang.
3.1 Language Issues
In working with Erlang we have been able to com-pare our experience with what we have done in writ-ing refactorings for Haskell. Erlang is a smaller lan-guage than Haskell, and in its pure functional part, verystraightforward to use. It does however have a number
Add a Comment