Title: Refactoring Haskell 98 Programs Vs. Refactoring Erlang Programs
1Refactoring Haskell 98 Programs
Vs.Refactoring Erlang Programs
- Huiqing Li
- Simon Thompson
2Outline
- Haskell 98 Vs. Erlang
- Behaviour and Layout preservation
- Renaming a variable/function name
- Generalise a definition
- Implementation
- Conclusion
3Haskell 98 Vs. Erlang
- Haskell 98 a lazy, statically typed, purely
functional programming language featuring,
higher-order functions, polymorphism, type
classes and monadic effects. - Erlang a strict, dynamically typed functional
programming language with support for
concurrency, communication, distribution and
fault-tolerance.
4Haskell 98 Vs. Erlang
- -- Erlang
- Functional
- Higher-order
- Pattern matching
- Module system
- General purpose language but better suited for
concurrent applications.
-- Haskell Functional Higher-order Pattern
matching Module system General purpose
5Haskell 98 Vs. Erlang
-- Factorial In Haskell. module Fact where fac
Int -gt Int fac 0 1 fac n ngt0 n fac(n-1)
Factorial In Erlang. -module (fact). -export
(fac/1). fac(0) -gt 1 fac(N) when N gt 0 -gt N
fac(N-1).
6Haskell 98 Vs. Erlang
- -- Erlang
- Strict
- Dynamically Typed
- Non-pure
- No explicit type signatures
- All named functions are top-level
- No partial application
- Layout insensitive
- Simpler comment rules
- Simpler module interface
-- Haskell Lazy Statically typed Pure Allows
explicit type signatures Nested function
declarations Allows Partial application Layout
sensitive Complex comment rules Complex module
interface
7Haskell 98 Vs. Erlang
- -- Erlang
- Concurrency
- Communication
- Fault-tolerance
- Distribution
- OTP Design principles
- Macro directives
- . . .
-- Haskell Function composition Type
declarations Type classes Monads ...
8Haskell Vs. Erlang -- refactoring
opportunities
Haskell Refactorings
Erlang Refactorings
9Haskell Vs. Erlang -- refactoring
opportunities
- Common refactorings renaming, generalising a
function definition, removing unused
functions/parameters, moving a definition from
one module to another, swapping the order of
arguments , etc. - Haskell-specific refactorings data-type
declaration or type class related refactorings,
etc. - Erlang-specific refactorings concurrency-related
refactorings, OTP-related refactoring, etc.
10Behaviour preservation
- Haskell
- The behaviour of the main function should not be
changed. - Erlang
- The behaviour of those exported functions
should not be changed.
11Program Layout preservation
- Haskell people tend stick to their own layout,
therefore standard pretty-printer does not help
too much. -
- Erlang people tend to accept the standard layout,
partially because most Erlang people use Emacs as
the editor, therefore standard pretty-printed is
acceptable. -
12Renaming a variable name
- Renames all and only those uses of the variable,
and not every use of the particular variable
name. - This refactoring should not cause name conflict,
or name capture. - This refactoring needs a clear picture of the
binding structure (for variables) of the program
under refactorings.
module Fact where fac m tfac m b
-- renaming m to b or b to m causes name
capture where tfac o k k tfac n k
n gt0 tfac (n-1) (nk) -- renaming n to k
causes name conflict b 1
13Renaming a variable name
- In Haskell
- A pattern does not contain applied occurrences of
variables - A variable is only associated with one binding
occurrence. - All patterns must be linear (no variable may
appear more than once) - In Erlang
- Binding occurrence of a variable will always be
in a pattern, but a pattern may also contain
applied occurrences of variables. - A variable may have more than one binding
occurrences. - Non-linear patterns are allowed.
bar(S) -gt case S of 1 -gt S1 3, S1 _ -gt
S1 4, S1 end, S1 S1.
14Renaming a function name
- Renames all and only those uses of the function,
and not every use of the particular function
name. - This refactoring should not cause name conflict,
name shadowing or name capture. - This refactoring needs a clear picture of the
binding structure (for functions) of the program
under refactorings.
15Renaming a function name
- In Haskell
- The nested function declarations
- A module can export not only the functions
defined in this module, but also those functions
imported by this module. - Function name ( module name) identifies a
function. - Function application function name followed by
arguments. - In Erlang
- All named functions are top-level.
- A module can only export those functions that are
defined in this module (i.e. no transitive
exporting). - Function name arity ( module name) identifies
a function. - Function application is much more complex.
16Renaming a function name
- Function applications expressions in Erlang
- Fun (E1, , En) where Fun is a function
name. - eg.
length(1,2,3). - E0(E1, ., En)
- case1 the value of E0 is a tuple. E.g. lists,
reverse(1,2,3) - case2 the value of E0 is an implicit fun
expression. - eg. (fun (X) -gt X 1 end) (2)
- case3 the value of E0 is an explicit fun
expression. - eg. (fun listsreverse/1) (1,2,3)
- EmE0(E1, , En) eg
listsreverse(1,2,3). - Meta function application in Erlang
- erlangapply ( Module, Fun, Args)
- eg. erlangapply (lists, reverse, a,b,c)
17Renaming a function name
- The flexibility of function application
expressions and meta function application in
Erlang make it hard or impossible to build a
complete call graph, or to clarify the binding
structure of the program under refactoring. - Example
-module (math). -export(f/1, f/2,f2/1). f
(X) -gt X. f (X, Y ) -gt X Y. f2(Args) -gt
erlangapply( math, f, Args).
18Generalise a function definition
- Generalise a definition by selecting a
sub-expression of the right-hand side and making
this the value of a new argument added to the
definition of the function or constant.
-module (test). -export(f/1). add_one
(N, HT) -gt HN add_one(N,T) add_one
(N,) -gt . f(X) -gt add_one(1, X).
19Generalise a function definition
- Erlang is a strict language, i.e. arguments are
evaluated before function application. - Erlang is not purely functional, mutable stuff
(message sends/receives and state dependent
responses) play an important part in most large
programs. - Strict non pure cause problems when generalise
a function on a subexpression that has
side-effects.
20Generalise a function definition
-module (test). -export(f/0). repeat(0) -gt
ok repeat(N) -gt ioformat (hello\n"),
repeat(N-1). f( ) -gt repeat(5).
-module (test). -export(f/0). repeat(A, 0) -gt
ok repeat(A, N) -gt A,
repeat(A,N-1). f( ) -gt repeat (ioformat
(hello\n), 5).
Function f/0 behaves differently before and after
transformation.
21Generalise a function definition
- Our solution using higher order functions.
-module (test). -export(f/0). repeat(0) -gt
ok repeat(N) -gt ioformat (hello\n"),
repeat(N-1). f( ) -gt repeat(5).
-module (test). -export(f/0). repeat(A, 0) -gt
ok repeat(A, N) -gt A( ),
repeat(A,N-1). f( ) -gt repeat (fun(
)-gtioformat (hello\n) end, 5).
Function f/0 behaves the same before and after
transformation.
22Generalise a function definition
- By using higher-order functions and the
flexibility of function application provide by
Erlang, we could also generalise a function on a
sub-expression which contains locally declared
variables.
-module (test). -export(b/1). broadcast (Msg,
Pid Pids) -gt Pid ! Msg, broadcast
(Msg, Pids) broadcast(_, ) -gt true. b (Ps )
-gt broadcast (The message, Ps).
23Generalise a function definition
-module (test). -export(b/1). broadcast (Msg,
Pid Pids) -gt Pid ! Msg, broadcast
(Msg, Pids) broadcast(_, ) -gt true. b (Ps )
-gt broadcast (The message, Ps).
-module (test). -export(b/1). broadcast (F,
Msg, Pid Pids) -gt F (Pid, Msg),
broadcast (F, Msg, Pids) broadcast(F, _, ) -gt
true. b (Ps ) -gt broadcast ( fun(Pid, Msg) -gt
Pid ! Msg end, The message, Ps).
24Implementation
- The Haskell Refactorer, HaRe is implemented in
Haskell, - The Erlang Refactorer is implemented in Erlang.
- In the implementation of HaRe, lots of effort
has been put to comment and layout preserving,
whereas this is not the case in the
implementation of the Erlang refactorer. - Type errors can be more easily detected during
the implementation of HaRe.
25Implementation
- The implementation of the Erlang Refactorer.
Program source
Scanner Parser Syntax Tools
AST with comments
Program analysis and transformation
Transformed AST
Pretty printer
Program source
26Conclusion
- Different language features expose different
refactoring opportunities. - Refactoring Erlang programs involve more
condition analyses. - OTP may impose some constraints on refactoring
particular programs. - Experience from refactoring Haskell program
helps to some extent. - Proof of behaviour preservation may be more
difficult for Erlang programs. -