Why does TCO require support from the VM? - clojure

Some VMs, most notably the JVM, are said to not support TCO. As a result, language like Clojure require the user to use loop recur instead.
However, I can rewrite self-tail calls to use a loop. For example, here's a tail-call factorial:
def factorial(x, accum):
if x == 1:
return accum
else:
return factorial(x - 1, accum * x)
Here's a loop equivalent:
def factorial(x, accum):
while True:
if x == 1:
return accum
else:
x = x - 1
accum = accum * x
This could be done by a compiler (and I've written macros that do this). For mutual recursion, you could simply inline the function you're calling.
So, given that you can implement TCO without requiring anything of the VM, why don't languages (e.g. Clojure, Armed Bear Common Lisp) do this? What have I missed?

Inlining is not a solution to the general tail call elimination problem for a number of reasons. The list below is not meant to be exhaustive. It is, however, sorted -- it starts with an inconvenience and ends with a complete showstopper.
A function called in a tail position may be a large one, in which case inlining it may not be advisable from a performance standpoint.
Suppose there are multiple tail calls to g in f. Under the regular definition of inlining, you'd have to inline g at each call site, potentially making f huge. If instead you choose to goto the beginning of g and then jump back, then you need to remember where to jump to and all of a sudden you're maintaining your own call stack fragment (which will almost certainly exhibit poor performance when compared to the "real" call stack).
For mutually recursive functions f and g, you'd have to inline f in g and g in f. Clearly this is impossible under the usual definition of inlining. So, you're left with what's effectively a custom in-function calling convention (as in the goto-based approach to 2. above).
Real TCE works with arbitrary calls in tail position, including in higher-order contexts:
(defn say-foo-then-call [f]
(println "Foo!")
(f))
This can be used to great effect in certain scenarios and clearly cannot be emulated with inlining.

It can be surprising, and may make debugging harder, since you can't see the call stack.
It only works in very special cases, rather than the general cases that are handled when the VM supports TCO.
Programmers often don't write their code tail-recursively, unless the language gives them incentive to do so. E.g. recursive factorial is usually written with the recursion step as n * fact(n-1), which is not tail-recursive.

TCO per se doesn't require VM support. That is to say, not for local functions. Tail calls spanning external functions require VM support. Ideally, a complete implementation of tail recursion allows functions in separately compiled program units to mutually recurse in constant space, not only functions that are local to one parent function, or functions that are all visible to the compiler at once.
In a VM without tail call support, function calls are encapsulated, and allocate a new frame on exit. Tail calls require a special entry point which bypasses that. Functions can participate in tail calling as well as non-tail calling, so they require both entry points.
Tail call elimination can be simulated without VM support using non-local returns and dispatch. That is to say, when a tail call takes place syntactically, it is translated to a non-local return which abandons the current function via dynamic control transfer, passing the arguments (packaged as an object, perhaps) to a hidden dispatch loop, which transfers control to the target function, passing it those arguments. This will achieve the requirement that recursion takes place in constant space and will "look and feel" like tail calling. However, it is slow, and perhaps not entirely transparent.

Related

What is the distinction between a procedure and a function when, in the object code functions are called procedurally?

My understanding is that for compiled languages such as C++, the compiler substitutes the line where the functions are called for the statements in the function body.
Why then, is there a difference between functions and procedures ? Since, a procedure is changing state in-line and so are functions, once compiled ?
pseudocode:
main()
{
let a = 1;
a = add_one(a);
}
fn add_one(n) {
n + 1
}
at compile time, something like... (apologies, attempting to write pseudocode from my memory of that assembly class ~10 years ago is hard)
ASSIGN 1 to A
ASSIGN A FN ADDONE A
FN ADDONE A // Essentially, the function is converted into a procedure
A + 1 // That is evaluated and substituted in-line
EXIT
the compiler substitutes the line where the functions are called for the statements in the function body
That is something a compiler can optionally perform, known as "procedural integration", or more commonly "inlining".
Inlining is not the normal way of implementing procedure calls. Firstly, inlining all calls would cause compiled programs to explode in size, and potentially to run slower. (Inlining can speed up programs, but only when judiciously applied: an example of where it may help is when a small function that is executed many times in a loop is hoisted into that loop.)
Secondly, calls can be recursive. Recursion cannot be implemented by inlining; it is logically impossible for a function to be inserted into itself: this leads to infinite regress.*
The bare essence of a procedure call is that the calling procedure is suspended, while the called procedure executes. When the called procedure finishes executing, the caller is resumed. This semantics is preserved under inlining; it still looks like the surrounding procedure is suspended while the inlined one executes.
A regular non-inlined procedure call compiles into some kind of control transfer, whereby the machine, instead of executing the next instruction, is told to "jump" to some other list of instructions, corresponding to the body of the procedure. The previous location is somehow remembered. When the called procedure finishes, it "jumps" back to the remembered location, so the calling procedure resumes from that point. When multiple places in the program call a non-inlined procedure, they all share the same image of that procedure: the same list of instructions.
Both procedures and pure functions are susceptible inlining optimizations; there is no difference in that regard. The semantics is preserved; the inlined call appears to be passing parameters and returning a value.
The terminology like procedure and function varies from programming language to programming language.
How the words are used in computer science is that a function corresponds to the mathematical concept of a pure mapping from one abstract space to another. A function returns a value that is calculated from some arguments, which is done without side effects.
A procedure is a name given to some steps which perform some effects, making changes to the state of the machine. Those steps are invoked by their name. Procedures may look like functions in that they have arguments, and return values.
Some languages, like C, call everything function: printf is called a "function", even though it has a side effect of performing output. exit and abort are "functions", though they destroy the process and do not return.
When procedures are called functions, you may hear the term "pure function"
in reference to functions that don't have side effects and only calculate a value.
Common Lisp uses the function terminology similarly to C; both functions and procedures are defined using defun and are called functions. Scheme uses the term "procedure". So even related language dialects don't necessarily agree.
You have to keep the concepts clearly separated from the way your programming language documentation refers to them. Don't assume that because your language reference manual says "function", that it's talking about a pure mapping between sets.
(There are subtleties in the "function" concept because in computer science, which is after all a branch of mathematics, "function" can refer to a mathematical function, or to a computational function which calculates some mathematical function using concrete algorithmic steps. The two are not the same because not all mathematical functions are computable by a corresponding computational function. For instance the "halting function" exists in mathematics, but cannot be computed. The halting function H has two arguments: a program P and an input I. It maps these to a Boolean value: true if the program P halts when given input I, or false if it doesn't halt. The mathematical function is real because every P(I) pair either halts or doesn't halt. But we cannot implement such a function.)
* Of course, partial inlining of recursion is possible. For instance, in the simple case of a function calling itself, a compiler could decide to inline some of the self-calls to some specific depth. The inlining will "bottom out", so that some of the calls remain non-inlined.

How does Clojure's optimiser work, and where is it?

I am new to Clojure, but not to lisp. A few of the design decisions look strange to me - specifically requiring a vector for function parameters and explicitly requesting tail calls using recur.
Translating lists to vectors (and vice versa) is a standard operation for an optimiser. Tail calls can be converted to iteration by rewriting to equivalent clojure before compiling to byte code. The [] and recur syntax suggest that neither of these optimisations are present in the current implementation.
I would like a pointer to where in the implementation I can find any/all source-to-source transformation passes. I don't speak Java very well so am struggling to navigate the codebase.
If there isn't any optimisation before function-by-function translation to the JVM's byte code, I'd be interested in the design rationale for this. Perhaps to achieve faster compilation?
Thank you.
There is no explicit optimizer package in the compiler code. Any optimizations are done "inline". Some can be enabled or disabled via compiler flags.
Observe that literal vectors for function parameters are a syntactic choice how functions are represented in source code. Whether they are represented as vectors or list or anything else would not affect runtime and cannot be optimized hence.
Regarding automatic recur, Rich Hickey explained his decision here:
When speaking about general TCO, we are not just talking about
recursive self-calls, but also tail calls to other functions. Full TCO
in the latter case is not possible on the JVM at present whilst
preserving Java calling conventions (i.e without interpreting or
inserting a trampoline etc).
While making self tail-calls into jumps would be easy (after all,
that's what recur does), doing so implicitly would create the wrong
expectations for those coming from, e.g. Scheme, which has full TCO.
So, instead we have an explicit recur construct.
Essentially it boils down to the difference between a mere
optimization and a semantic promise. Until I can make it a promise,
I'd rather not have partial TCO.
Some people even prefer 'recur' to the redundant restatement of the
function name. In addition, recur can enforce tail-call position.
specifically requiring a vector for function parameters
Most other lisps build structures out of syntactic lists. For an associative "map" for example, you build a list of lists. For a "vector", you make a list. For a conditional switch-like expression, you make a list of lists of lists. Lots of lists, lots of parenthesis.
Clojure has made it an obvious goal to make the syntax of lisp more readable and less redundant. A map, set, list, vector all have their own syntax delimiters so they jump out at the eye, while also providing specific functionality that otherwise you'd have to explicitly request using a function if they were all lists. In addition to these structural primitives, other functions like cond minimize the parentheses by removing one layer of parentheses for each pair in the expression, rather than additionally wrapping each pair in yet another grouped parenthesis. This philosophy is widespread throughout the language and its core library so the code is more readable and elegant.
Function parameters as a vector are just part of this syntax. It's not about whether the language can convert a list to a vector easily, it's about how the language requires the placement of function parameters in a function definition -- and it does so by explicitly requiring a vector. And in fact, you can see this clearly in the source for defn:
https://github.com/clojure/clojure/blob/clojure-1.7.0/src/clj/clojure/core.clj#L296
It's just a requirement for how a function is written, that's all.

In what languages can you redefine methods/functions in terms of themselves?

I'm interested in trying literate programming. However, I often that the requirements are stated in a general but then exceptions are given much later.
For example in one section it will say something like Students are not allowed in the hallways while classes are in session.
But then later there will be section where it says something like Teachers may give a student a hall pass at which point the student may be in the hall while class is in session.
So I'd like to be able to define allowedInTheHall following the first section so that it doesn't allow students in the hall, but then after the second section redefines allowedInTheHall so that it first checks for the presence of a hall pass, and if it's missing then delegates back to the previous definition.
So the only way I can imagine this working would be a language where:
you can redefine a method/function/subroutine in terms of it's previous definition
where only the latest version of a function gets called even if the caller was defined before the latest redefinition of the callee (I believe this is called "late binding").
So which languages fulfill support these criteria?
PS- my motivation is that I am working with existing requirements (in my case game rules) and I want to embed my code into the existing rules so that the code follows the structure of the rules that people are already familiar with. I assume that this situation would also arise trying to implement a legal contract.
Well to answer the direct question,
you can redefine a method/function/subroutine in terms of it's previous definition
...in basically any language, as long as it supports two features:
mutable variables that can hold function values
some kind of closure forming operator, which effectively amounts to the ability to create new function values
So you can't do it in C, because even though it allows variables to store function pointers, there's no operation in C that can compute a new function value; and you can't do it in Haskell because Haskell doesn't let you mutate a variable once it's been defined. But you can do it in e.g. JavaScript:
var f1 = function(x) {
console.log("first version got " + x);
};
function around(f, before, after) {
return function() {
before(); f.apply(null, arguments); after();
};
}
f1 = around(f1,
function(){console.log("added before");},
function(){console.log("added after");});
f1(12);
or Scheme:
(define (f1 x) (display "first version got ") (display x) (newline))
(define (around f before after)
(lambda x
(before) (apply f x) (after) ))
(set! f1 (around
f1
(lambda () (display "added before") (newline))
(lambda () (display "added after") (newline))))
(f1 12)
...or a whole host of other languages, because those are really rather common features. The operation (which I think is generally called "advice") is basically analogous to the ubiquitous x = x + 1, except the value is a function and the "addition" is the wrapping of extra operations around it to create a new functional value.
The reason this works is that by passing the old function in as a parameter (to around, or just a let or whatever) the new function is closing over it referred to through a locally scoped name; if the new function referred to the global name, the old value would be lost and the new function would just recurse.
Technically you could say this is a form of late binding - the function is being retrieved from a variable rather than being linked in directly - but generally the term is used to refer to much more dynamic behaviour, such as as JS field access where the field might not even actually exist. In the above case the compiler can at least be sure the variable f1 will exist, even if it turns out to hold null or something, so lookup is fast.
Other functions that call f1 would work the way you expect assuming that they reference it by that name. If you did var f3 = f1; before the around call, functions defined as calling f3 wouldn't be affected; similarly objects that got a hold of f1 by having it passed in as a parameter or something. Basic lexical scoping rule applies. If you want such functions to be affected too, you could pull it off using something like PicoLisp... but you're also doing something you probably shouldn't (and that's not any kind of binding any more: that's direct mutation of a function object).
All that aside, I'm not sure this is in the spirit of literate programming at all - or for that matter, a program that describes rules. Are rules supposed to change depending on how far you are through the book or in what order you read the chapters? Literate programs aren't - just as a paragraph of text usually means one thing (you may not understand it, but its meaning is fixed) no matter whether you read it first or last, so should a declaration in a true literate program, right? One doesn't normally read a reference - such as a book of rules - from cover to cover like a novel.
Whereas designed like this, the meaning of the program is highly dependent on being read with the statements in one specific order. It's very much a machine-friendly series-of-instructions... not so much a reference book.

What's the convention for using an asterisk at the end of a function name in Clojure and other Lisp dialects?

Note that I'm not talking about ear muffs in symbol names, an issue that is discussed at Conventions, Style, and Usage for Clojure Constants? and How is the `*var-name*` naming-convention used in clojure?. I'm talking strictly about instances where there is some function named foo that then calls a function foo*.
In Clojure it basically means "foo* is like foo, but somehow different, and you probably want foo". In other words, it means that the author of that code couldn't come up with a better name for the second function, so they just slapped a star on it.
Mathematicians and Haskellers can use their apostrophes to indicate similar objects (values or functions). Similar but not quite the same. Objects that relate to each other. For instance, function foo could be a calculation in one manner, and foo' would do the same result but with a different approach. Perhaps it is unimaginative naming but it has roots in mathematics.
Lisps generally (without any terminal reason) have discarded apostrophes in symbol names, and * kind of resembles an apostrophe. Clojure 1.3 will finally fix that by allowing apostrophes in names!
If I understand your question correctly, I've seen instances where foo* was used to show that the function is equivalent to another in theory, but uses different semantics. Take for instance the lamina library, which defines things like map*, filter*, take* for its core type, channels. Channels are similar enough to seqs that the names of these functions make sense, but they are not compatible enough that they should be "equal" per se.
Another use case I've seen for foo* style is for functions which call out to a helper function with an extra parameter. The fact function, for instance, might delegate to fact* which accepts another parameter, the accumulator, if written recursively. You don't necessarily want to expose in fact that there's an extra argument, because calling (fact 5 100) isn't going to compute for you the factorial of 5--exposing that extra parameter is an error.
I've also seen the same style for macros. The macro foo expands into a function call to foo*.
a normal let binding (let ((...))) create separate variables in parallel
a let star binding (let* ((...))) creates variables sequentially so that can be computed from eachother like so
(let* ((x 10) (y (+ x 5)))
I could be slightly off base but see LET versus LET* in Common Lisp for more detail
EDIT: I'm not sure about how this reflects in Clojure, I've only started reading Programming Clojure so I don't know yet

How to detect correct function call pairs

I am looking a tool able to detect ordered function call pairs in a nested fashion as shown below:
f() // depth 0
f() //depth 1
g()
g()
At each depth of call f() there must be a call of g() forming function call pair. This is particularly important in critical section entry and exit.
In C++, one option is to wrap the calls to f() and g() in the constructor and destructor of a class and only call those functions by instantiating an instance of that class. For example,
struct FAndGCaller
{
FAndGCaller() { f(); }
~FAndGCaller() { g(); }
};
This can then be used in any scope block like so:
{
FAndGCaller call_f_then_later_g; // calls f()
} // calls g()
Obviously in real code you'd want to name things more appropriately, and often you'll simply want to have the contents of f() and g() in the constructor and destructor bodies, rather than in separate functions.
This idiom of Scope Bound Resource Management (SBRM, or more commonly referred to as Resource Acquisition is Initialization, RAII) is quite common.
You may abuse a for-loop for this.
#define SAVETHEDAY for (bool seen = ((void)f(), true); seen; seen = ((void)g(), false))
The comma operator always lets your functions f be executed before the dependent statement and g afterwards. E.g
SAVETHEDAY {
SAVETHEDAY {
}
}
Pros:
Makes nesting levels clear.
Works for C++ and C99.
The pseudo for-loop will be
optimized away by any decent
compiler.
Cons:
You might have surprises with
break, return and continue
inside the blocks, so g might not be called in such a situation.
For C++, this is not safe against a
throw inside, again g might not be called
Will be frowned upon by many people since is in some sort extending the language(s).
Will be frowned upon by many people
especially for C++ since generally
such macros that hide code are
thought to be evil
The problem with continue can be repaired by doing things a bit more cleverly.
The first two cons can be circumvented in C++ by using a dummy type as for-variable that just has f and g in the constructor and destructor.
Scan through the code (that's the hard part) and every time you see an invocation of f(), increment a counter. Every time you see an invocation of g(), decrement the counter. At the end, the counter should be back to zero. If it ever goes negative, that's a problem as well (you had a call to g() that wasn't preceded by a matching call to f()).
Scanning the code accurately is the hard part though -- with C and (especially) C++, writing code to understand source code is extremely difficult. Offhand, I don't know of an existing tool for this particular job. You could undoubtedly get clang (for one example) to do it, but while it'll be a lot easier than doing it entirely on your own, it still won't be trivial.
The Coccinelle tool for semantic searching and patching of C code is designed for this sort of task (see also this LWN article on the tool).