Inspired by my observation that tail calls can be seen as "goto with arguments"[1], in 2013 I wrote a Common Lisp macro called argtags which is like tags/prog, but with labels that have parameters. Giving them arguments causes the corresponding variables to be assigned as the transfer takes place. It compiles to tagbody.
Then, I used argtags as the basis for tlet: a macro that looks like it is defining local functions using syntax similar to labels or flet, but actually compiles to argtags, so everything ends up as a tagbody.
The calls are always tail calls, whether or not in tail position. You cannot accidentally blow the stack, but you can accidentally expect a call to return, which it won't!
There is a complementary cross-module tail calling system in here which provides a defun-like construct deftail for defining tail calling functions. That's based on a trampoline dispatch loop: tail calls unwind up to the loop, passing their arguments to it, along with the next function to be called.
[1] Later I found out that Steele also offered the viewpoint back in the 1970's that tail calls can be seen as goto with arguments.
Then, I used argtags as the basis for tlet: a macro that looks like it is defining local functions using syntax similar to labels or flet, but actually compiles to argtags, so everything ends up as a tagbody.
The calls are always tail calls, whether or not in tail position. You cannot accidentally blow the stack, but you can accidentally expect a call to return, which it won't!
This is all found here:
http://www.kylheku.com/cgit/lisp-snippets/tree/tail-recursio...
There is a complementary cross-module tail calling system in here which provides a defun-like construct deftail for defining tail calling functions. That's based on a trampoline dispatch loop: tail calls unwind up to the loop, passing their arguments to it, along with the next function to be called.
[1] Later I found out that Steele also offered the viewpoint back in the 1970's that tail calls can be seen as goto with arguments.