Next: Documentation, Previous: Evaluation, Up: Programming Aids
Expressions within functions can be automatically “refactored” into
their own sub-functions by using the erl-refactor-subfunction
command (C-c C-d f). The command takes the text of the
expression, determines which variables it needs from the original
function, and then generates the new function and puts it on the kill
ring for insertion by hand (with yank
, C-y). The original
function is rewritten with a call to the subfunction where the
refactored expression used to be.
For example, suppose we want to refactor the following function:
eval_expression(S) -> case parse_expr(S) of {ok, Parse} -> case catch erl_eval:exprs(Parse, []) of {value, V, _} -> {ok, flatten(io_lib:format("~p", [V]))}; {'EXIT', Reason} -> {error, Reason} end; {error, {_, erl_parse, Err}} -> {error, Err} end.
In this example we will take the inner case
expression and move
it into a new function called try_evaluation
. We do this by
setting the Emacs region (using the mouse or C-SPC) from the
word case
until the end of the word end
– marking
exactly one whole expression. We then enter C-c C-d f to
refactor, and when prompted for the function name we respond with
“try_evaluation
”. The original function is then rewritten to:
eval_expression(S) -> case parse_expr(S) of {ok, Parse} -> try_evaluation(Parse); {error, {_, erl_parse, Err}} -> {error, Err} end.
And at the front of the kill ring we have the new function definition, which can be pasted into the buffer wherever we want. The actual definition we get is:
try_evaluation(Parse) -> case catch erl_eval:exprs(Parse, []) of {value, V, _} -> {ok, flatten(io_lib:format("~p", [V]))}; {'EXIT', Reason} -> {error, Reason} end.
Important note: This command is not a “pure” refactoring,
because although it will import variables from the parent function
into the subfunction, it will not export new bindings created in the
subfunction back to the parent. However, if you follow good
programming practice and never “export” variables from inner
expressions, this is not a problem. An example of bad code that
will not refactor correctly is this if
expression:
if A < B -> X = true; B > A -> X = false end, foo(X)
This is in poor style – a variable created inside the if
is
used by code at an outer level of nesting. To work with refactoring,
and to be in better style, it should be rewritten like this:
X = if A < B -> true; B > A -> false end, foo(X)