[gclist] Finalization and object orientation.
Richard A. O'Keefe
ok@cs.rmit.edu.au
Wed, 2 Apr 1997 12:42:15 +1000 (EST)
Consider the following ML function (simulating N phases of a typical
compiler):
fun compile (src) =
let val codeInIL1 = phase1(src)
val codeInIL2 = phase2(codeInIL1)
......
val codeInILN = phaseN(codeInILNm1)
in codeInILN
end
What we concluded is the following: for languages that make pervasive uses
of "functions" and/or "closures", a new scoping rule must be used. Each
local variable should be considered "dead" after its *last* use in the
current function body. By "dead", I really mean "not contributing to the
liveness of the data structure it points to".
Let me put this in Prolog terms:
compile(T0, T) :-
phase1(T0, T1),
phase2(T1, T2),
...,
phaseN(Tn, T).
David Warren, in the early 80s spoke of "environment trimming";
his Prolog compiler (based on the WAM, it became Quintus Prolog)
basically does
T1 := unbound
...
Tn := unbound
R1 := T0, R2 := T1
*remove T0 from the environment if possible
call phase1/2
...
R1 := Tn, R2 := T
*remove the entire environment if possible
jump phaseN/2
The "if possible" refers to the need to handle backtracking.
(I don't know what the Mercury compiler does, but since it has more
information, it should do even better.)
To this day Quintus Prolog continues to do this. It's particularly
important in a logic programming language, because you get a lot more
variables than you would in a functional language.