Efficient Closure Utilisation by Higher-Order Inheritance Analysis

Higher-order functions and the ability to create new functions at runtime, either by partial application or λ-abstraction of existing functions, are important features of functional languages. Since these runtime-created functions may outlive their creating functions, it is necessary to represent all functional parameters by closures in a memory area which is not affected by the termination of function calls, i.e. in a heap component. Such a closure contains the original function, which may be a closure again, and the bindings of the parameters used (by the partial application). It many cases, however, it is possible to avoid this expensive heap allocation and use the run-time stack or even statically allocated closures. Often a closure created for a partial application is used only locally and will never be part of the result of the creating function. In these cases, the closure may be allocated on the stack. This approach is feasible in eager and lazy languages. If we can additionally ensure that there will never be more than one incarnation of a partial application, a closure can be allocated statically. In order to use this optimisation in lazy languages, we have to perform a strictness analysis first. We develop an abstract interpretation for the detection of inheritance information which allows us to decide whether the heap cells of an argument may be propagated to the result of a function call (i.e. are part of the result). Furthermore we give a criterion for checking whether there will be always only one incarnation of a certain partial application during runtime. In order to increase efficiency, we show how the number of recomputations can be decreased by using only parts of the abstract domains. The worst case time complexity is essentially quadratic in the size of the program. We illustrate the method developed in this paper with several examples. Correctness of the analysis is considered, using a modified denotational semantics as reference point. The main goal of our work is to keep both the run-time and the compile-time overhead as small as possible.