Skip to main content
Log in

Reversible Garbage Collection for Reversible Functional Languages

  • Research Paper
  • Published:
New Generation Computing Aims and scope Submit manuscript

Abstract

Reversible functional languages have been proposed that use patterns symmetrically for matching and building data: A pattern used on the left-hand side of a function rule takes apart a data structure, and a pattern used on the right-hand side of a rule builds a data structure. When calling a function in reverse, the meaning of patterns are reversed, so patterns on the right-hand side take apart data and patterns on the left-hand side build data. If using a pattern to build data creates a node with exactly one reference, a node that is taken apart must by symmetry also have exactly one reference. This implies linearity: A node that is built or taken apart has exactly one incoming reference. A recursive function can take apart one data structure while building two or more new copies, allowing multiple uses of values, but the new copies will also have one reference each. Linearity makes garbage collection simple: Whenever a node is taken apart by pattern matching, it is freed. The cost is that multiple uses of a value require deep copies. The reason for the linearity restriction is the symmetry between constructing and deconstructing nodes: Since constructing a node creates exactly one reference to this node, the inverse can only be applied if the reference count is exactly one. We can overcome this limitation if constructing a node can return a node with multiple references. We achieve this through maximal sharing: If a newly constructed node is identical to an already existing node, we return a pointer to the existing node (increasing its reference count) instead of allocating a new node with reference count one. This allows multiple references to nodes while retaining the symmetry of pattern matching and construction, and it allows values to be used multiple times without making deep copies. To avoid searching the entire heap for an identical node, we use hash-consing to restrict the search to a small segment of the heap. We estimate how large this segment needs to be to give a low probability of allocation failure with acceptable utilisation and support this estimate with experiments. We sketch how a functional program can be translated to use the memory manager, and we test the memory manager both with artificial test programs and a hand-compiled functional program.

This is a preview of subscription content, log in via an institution to check access.

Access this article

Price excludes VAT (USA)
Tax calculation will be finalised during checkout.

Instant access to the full article PDF.

Fig. 1
Fig. 2
Fig. 3
Fig. 4
Fig. 5
Fig. 6
Fig. 7
Fig. 8

Similar content being viewed by others

References

  1. Axelsen, H.B., Glück, R.: Reversible representation and manipulation of constructor terms in the heap. In: Dueck, G.W., Miller, D.M. (eds.) Reversible Computation. Volume 7948 of Lecture Notes in Computer Science, pp. 96–109. Springer, Berlin (2013)

  2. Baker HG (1992) Nreversal of fortune—the thermodynamics of garbage collection. In: Bekkers, Y., Cohen, J. (eds.) Memory Management. Lecture Notes in Computer Science, vol. 637, pp. 507–524. Springer, Berlin Heidelberg (1992)

  3. Broder, A.Z., Mitzenmacher, M.: Using multiple hash functions to improve IP lookups. In INFOCOM, pp. 1454–1463. IEEE, (2001)

  4. Cezzar, R.: Design of a processor architecture capable of forward and reverse execution. In: Proceeding of IEEE SOUTHEASTCON‘91, vol. 2, pp. 885–890 (1991)

  5. Cservenka, M.H., Glück, R., Haulund, T., Mogensen, T.Æ.: Data structures and dynamic memory management in reversible languages. In Proceedings of Reversible Computation 2018 (RC 2018). Lecture Notes in Computer Science. Springer, Berlin (2018) (To appear)

  6. Ershov, A.P.: On programming of arithmetic operations. Commun. ACM 1(8), 3–6 (1958)

    Article  MATH  Google Scholar 

  7. Goto, E.: Monocopy and associative algorithms in an extended LISP. Technical Report TR 74-03, University of Tokyo, (1974)

  8. Goubault, J.: Implementing functional languages with fast equality, sets and maps: an exercise in hash consing. Technical Report, Bull S.A. Research Center, rue Jean-Jaur‘es, 78340 Les Clayes sous Bois, (1994)

  9. Hansen, J.S.K.: Translation of a reversible functional programming language. Master’s thesis, DIKU, University of Copenhagen, December (2014)

  10. James, R.P., Sabry, A.: Theseus: a high-level language for reversible computation. In: Reversible Computation—Booklet of Work-in-Progress and Short Reports. http://www.reversible-computation.org (2014)

  11. Jenkins, B.: Hash functions. Dr. Dobb’s J. Softw. Tools 22(7), 107 (1997)

    Google Scholar 

  12. Knowlton, Kenneth C.: A fast storage allocator. Commun. ACM 8(10), 623–624 (1965)

    Article  MATH  Google Scholar 

  13. Knuth, D.E.: The art of computer programming, volume 1 (3rd ed.): fundamental algorithms. Addison Wesley, Redwood City (1997)

    MATH  Google Scholar 

  14. Lehman, E., Panigrahy, R.: 3.5-Way Cuckoo Hashing for the Price of 2-and-a-Bit, pp. 671–681. Springer, Berlin (2009)

    MATH  Google Scholar 

  15. Janus, C.L.: A time-reversible language. A letter to Landauer. http:://www.tetsuo.jp/ref/janus.pdf (1986)

  16. Mogensen, T. Æ.: Partial evaluation of Janus part 2: assertions and procedures. In: Clarke, E., Virbitskaite, I., Voronkov, A. (eds.) Perspectives of Systems Informatics. Lecture Notes in Computer Science, vol. 7162, pp. 289–301. Springer, Berlin (2012)

  17. Mogensen, T. Æ.: Reference counting for reversible languages. In: Reversible Computation. Lecture Notes in Computer Science. Springer, Berlin Heidelberg (2014)

  18. Mogensen, T.Æ.: Garbage collection for reversible functional languages. In: Proceedings of Reversible Computation 2015 (RC 2015). volume 9138 of Lecture Notes in Computer Science, pp. 79–94. Springer, Berlin (2015)

  19. Oh, C.W.: Reversible intermediate language for the translation of reversible programming languages. Master’s thesis, DIKU, University of Copenhagen, (2009)

  20. Pagh, R., Rodler, F.F.: Cuckoo hashing. J. Algorithms 51(2), 122–144 (2004)

    Article  MathSciNet  MATH  Google Scholar 

  21. Raab, M., Steger, A.: “Balls into bins”—a simple and tight analysis. In: Proceedings of the Second International Workshop on Randomization and Approximation Techniques in Computer Science, RANDOM ’98, pp. 159–170. Springer, London, UK, UK, (1998)

  22. Thomsen, M.K., Axelsen, H.B., Glück, R:. A reversible processor architecture and its reversible logic design. In: Alexis De, V., Wille, R. (eds.) RC 2011, volume 7165 of Lecture Notes in Computer Science, pp. 30–42. Springer, (2011)

  23. Yokoyama, T., Axelsen, H.B., Glück, R:. Towards a reversible functional language. In: Alexis, V., Robert, W. (eds.) Reversible Computation. Lecture Notes in Computer Science, vol. 7165, pp. 14–29. Springer, Berlin (2012)

Download references

Author information

Authors and Affiliations

Authors

Corresponding author

Correspondence to Torben Ægidius Mogensen.

Appendices

Appendix: Formal Semantics of RIL

Figure 9 shows a formal semantics for execution of RIL as rules for state transitions. A state consists of the program P (which never changes), an environment \(\rho \), that maps named variables to integers, a memory store \(\sigma \), that maps word-aligned addresses to integers, a stack S that stores return labels, and the current label l. A transition of the form \(P~\rho ~\sigma ~S~l \rightleftharpoons P~\rho '~\sigma '~S~l'\) states that a state \(P~\rho ~\sigma ~S~l\) will lead to the state \(P~\rho '~\sigma '~S~l'\) in one or more steps.

The semantics shows how execution of a basic block makes a transition from a label to another while changing the environment and store. The transition is bidirectional, so it describes both forward and backward execution. This is used in the rule for uncall, where the transition relation is used in the reverse order for executing the subroutine.

We use a (possibly subscripted) meta variable I to indicate an unspecified instruction (update or exchange), E to indicate an unspecified entry point, X to indicate an unspecified exit point and c to indicate an unspecified condition. Abusing notation, we use \(\oplus \), \(\odot \) and \(\bowtie \) to represent both the syntactic and semantic versions of operators. We use the same rules for evaluating expressions and conditions, using 0 to represent false and any non-zero value to represent true.

Fig. 9
figure 9

Semantics of RIL

Fig. 10
figure 10

Grammar for a small functional language

Appendix: Compilation Scheme

For reasons of space, we show a compilation scheme for a subset of the language we have used for examples. This is intended to show that the memory manager presented in this paper is suitable for functional languages.

A grammar for the language is found in Fig. 10. We use the following restrictions, that we assume hold, to ensure reversibility:

  • If a function definition has more than one rule, these cannot have overlapping patterns, neither for inputs or outputs.

  • A variable defined in a pattern must be used exactly once in a pattern. Patterns that define variables are patterns for arguments in a function definition or patterns for results of function calls in let-definitions. Patterns that use variables are patterns for function results or for arguments to function calls in let-definitions.

To allow multiple uses of values, we use a special function copy that takes a value as input and returns a cons-pair with two copies of that value. In reverse, it takes a cons-pair of two identical values and returns one of these. It is undefined if the argument is not a pair of two identical values. A compiler can insert such calls when a variable is used multiple times.

A function takes a single argument and returns a single result. A let-definition of the form let \(~f~p_1~=~p_2~\) in represents calling f backwards with \(p_2\) as argument and \(p_1\) as result. A similar notation is used in RFUN2 [17] and Theseus [10].

In the compiled code, the argument is passed in the variable A, and the result is passed in R. A constant is any pattern not containing variables. Symbols and numbers are represented as number constants, for example, the empty list [] is represented by the value 2 and the integer 3 is represented as the value 7 (i.e., 3<<1+1). More complex constants are evaluated into named variables in an initialisation function (such as nilnil as in Fig. 8). A pattern of the form x  !=  p is a guard: It matches any value that does not match the pattern p and binds that value to x. No variables in p are ever bound, so they cannot be used in the rule. For example, the pattern x != y :: z matches when the value is not a pair, and binds x to that value if so. In no cases are y or z bound.

Fig. 11
figure 11

Compilation scheme for functions

Fig. 12
figure 12

Compilation scheme for let-definitions

The functions in a program are compiled independently of each other. It is assumed that names in the program are renamed so they do not clash with names used by the memory manager or the compiler. Compilation schemes for functions, patterns, and let-definitions are shown in Figs. 11, 12, and 13. We use \(\overline{c}\) to represent the inversion of the code c, as described in Sect. 3.4. As this is a local inversion, call is inverted to uncall and vice versa.

Note that the code in Fig. 8 does not quite follow the compilation scheme presented here, as many simplifications are made to fit the code on one page.

Fig. 13
figure 13

Compilation scheme for patterns

About this article

Check for updates. Verify currency and authenticity via CrossMark

Cite this article

Mogensen, T.Æ. Reversible Garbage Collection for Reversible Functional Languages. New Gener. Comput. 36, 203–232 (2018). https://doi.org/10.1007/s00354-018-0037-3

Download citation

  • Received:

  • Accepted:

  • Published:

  • Issue Date:

  • DOI: https://doi.org/10.1007/s00354-018-0037-3

Keywords

Navigation