wiki:RefactoringSteps/GeneralizeFunction

Version 1 (modified by manualwiki, 13 years ago) (diff)

--

Generalize function

This refactoring generalizes a function definition by selecting an expression (or an expression sequence), and makes this the value of a new parameter added to the definition of the function. The actual parameter at every call site becomes the selected part with the corresponding compensation.

The generalized function will not be exported from the module. If the original function is exported from the module, a new function definition with the original arity will be created which calls the generalized function with the corresponding arguments.

In the example a function for double the elements of a list is generalized by selecting the subexpression 2. The subexpression is replaced with the variable N and the subexpression is added to the argument-list of function call in the body of f(Z) function.

Introduce a new parameter to function double/1:

-module(gen1).
-export([double/1]).

double(List) ->
   [2*X || X <- List].

f(Z)->
   double(Z).

Result:

-module(gen1).
-export([double/1]).

double(List) ->
   double(List,2)

double(List, N) ->
   [N*X || X <- List]

f(Z)->
   double(Z, 2).

Side conditions

  • The name of the function with its arity increased by one should not conflict with another function, either defined in the same module, imported from another module, or being an auto-imported built-in function.
  • The new variable name does not exist in the scope of the selected expression(s) and must be a legal variable name. If the new variable name does not keep these conditions, the transformation starts an interaction to ask for a new variable name.
  • The starting and ending positions should delimit an expression or a sequence of expressions.
  • The selected expressions do not bind variables that are used outside the selection.
  • Variable names bound by the expressions do not exist in the scopes of the generalized function calls.
  • The expressions to generalize are not patterns and they do not call their containing function.
  • If the selection is part of a list comprehension, it must be a single expression and must not be the generator of the comprehension.
  • The extracted sequence of expressions are not part of a macro definition, and are not part of macro application parameters.

Transformation steps and compensations

  1. If the selected expression does not contain any variables:
    • Give an extra argument (a simple variable) to the function. The name of this argument is the name given as a parameter of the transformation.
    • Replace the expression with a variable expression.
    • Add the selected expression to the argument list of every call of the function.
  1. If the selected expression contains variable(s) or contains more than one expression or has a side-effect:
    • Add a new argument (a simple variable) to the function definition with the given variable name.
    • Replace the expression with an application. The name of the application is the given variable name, the arguments of the application are the contained variables.
    • Add a fun expression to the argument list of every call of the function. The parameters of the fun expression are the contained variables and the body is the selected expression.
  1. If the selected expression is part of a guard expression:
    • Give an extra argument (a simple variable) to the function. The name of this argument is the name given as a parameter of the transformation.
    • Replace the guard expression with a variable expression with that new name.
    • Add the selected expression to the argument list of the function calls.
    • If the selected expression contain formal parameter, replace this with the actual parameter.
    • If the selected guard is a conjunction or a disjunction, create an andalso or an orelse expression and add this to the argument list of the function call.
  1. If the generalized function is recursive, than instead of add the selection to the argument list of the function calls in the body, add the new parameter to the argument list.
  1. If the generalized function is exported from the module, add a new function definition to the current module with the same parameters and guard. The body of this function is a call of the generalized function.
  1. If the selection contains macro application/record expression move the definition of the macro/record before the first call of function.