
Tail Recursion and Debugging (2011) - lelf
http://funcall.blogspot.com/2011/03/tail-recursion-and-debugging.html
======
rjmunro
You don't get a stack trace from an ordinary loop, but somehow we manage. As
long as it is clear that tail recursion happened, not having a complete stack
trace doesn't seem like the end of the world.

~~~
braythwayt
The equally interesting implication of your comment that "we don't get a stack
trace from an ordinary loop" is that maybe we should have logging and
introspection the behaviour of loops, and the fact that we have them for
function invocation but not for loops is combination of a historical accident
and a lack of interest in rethinking how languages are implemented.

~~~
pkaye
What information should be logged? And if the loop iterates for a million
times, should we log it every time?

~~~
Jtsummers
It could be an option when debugging like adding a trace to your function
calls. And the amount of tracing (how much is stored) could be configured.

Depending on the loop syntax, presumably you'd want to know (in the case of a
for loop) what variables are initialized, what the test condition is and its
result, and what is altered. But, in the case of C for loops as an example,
this wouldn't be easy to automate since the user can always do something like:

    
    
      for(;;) {
        if(condition) break;
        // other logic
      }
    

In the best case (which would be the most data-ful case and hardest to use)
you'd log all changes that occur within the loop. What you really want would
be smaller, like detecting the break and the condition leading to it and watch
that.

More realistically, you probably want to document your loop invariants with
assertions which could be optionally recorded in your trace output.

~~~
remram
This sounds like watchpoints which can be used both for loops and
(tail-)recursive functions.

------
bachmeier
> Tail recursion can be easily inlined, however it is my understanding that
> the Java creators specifically chose not to implement this, as it makes
> resultant code hard to debug (if not impossible).

That has always been a strange argument. You can turn features on and off, so
the "it's hard to debug" argument doesn't make sense. Scala added @tailrec and
I don't think I've ever heard complaints about it. Clojure has recur. Both are
JVM languages.

------
thamer
For the JVM it's worth noting that Project Loom[1], while so far focused
mainly on concurrency features like virtual threads and continuations also
includes a proposal to implement better tail calls in the JVM:

> it is also the goal of this project to add an even lighter-weight construct
> that will allow unwinding the stack to some point and then invoke a method
> with given arguments (basically, a generalization of efficient tail-calls).
> We will call that feature unwind-and-invoke, or UAI. It is not the goal of
> this project to add an automatic tail-call optimization to the JVM.

Since this paragraph is explicit about the feature not being "automatic",
it'll be interesting to see what form this takes.

[1] [https://cr.openjdk.java.net/~rpressler/loom/Loom-
Proposal.ht...](https://cr.openjdk.java.net/~rpressler/loom/Loom-
Proposal.html)

------
a1369209993

      > at com.alba.vware.ManagedServletPipeline.service(ManagedServletPipeline.java:91)
      > at com.alba.vware.FilterChainInvoke.doFilter(FilterChainInvoke.java:62)
      > at com.alba.vware.FilterDefinition.doFilter(FilterDefinition.java:167)
      > at com.alba.vware.FilterChainInvoke.doFilter(FilterChainInvoke.java:58)
      > (82 repetitions of 2 lines elided) [sic]
      > at com.alba.vware.FilterDefinition.doFilter(FilterDefinition.java:167)
      > at com.alba.vware.FilterChainInvoke.doFilter(FilterChainInvoke.java:58)
      > at com.alba.vware.ManagedFilterPipeline.dispatch(ManagedFilterPipeline.java:118)
      > at com.alba.vware.Filter.doFilter(Filter.java:113)
    

This is a tooling problem.

------
pizlonator
This is like some kind of artwork, and it’s a masterpiece.

Whether you want to see tail deleted frames or not depends a lot on how you
code. I can appreciate both sides of the argument and haven’t ever really made
up my own mind.

------
grantjpowell
This is satire right? The author's point is that this stack trace isn't very
helpful because it's repeating the same thing over and over?

~~~
mcguire
The trace isn't repeating. Those are individual call stack records. It is just
printing the trace as it is.

~~~
CyberDildonics
They didn't say the trace was repeating.

------
nonbirithm
I personally find it annoying that you can't create a full CPU benchmarking
tree from function calls in LuaJIT because the debug information in tail calls
is elided. That means you have to guess if a function return went up the stack
multiple times and it doesn't always work. So now I'm trying to figure out
other ways of profiling my code.

------
gumby
There’s no reason a -g build couldn’t maintain a small stack of arg
breadcrumbs which would go a long way to address this. Or simply don’t make a
tail call for debugging builds (might be hard when you use tail call for large
iterations though)

------
thewisenerd
oh;

I remember debugging something similar on maven due to a circular dependency
somehow that only broke my team's project;

ran mvnDebug, connecting jdb; stacktrace crapped out; dumping variables, and
adding the appropriate <exclusion> worked :D

that was the highlight of my sunday evening.

[https://github.com/apache/maven-
resolver/blob/d1d30018b990f7...](https://github.com/apache/maven-
resolver/blob/d1d30018b990f76e8a7b463988b1abbdc3d6944f/maven-resolver-
util/src/main/java/org/eclipse/aether/util/graph/transformer/NearestVersionSelector.java#L158)

------
thewisenerd
it's funny how there's a framesInCommon removal for printing out stack traces;
but something similar _could_ likely be added for x lines repeating y times
continuously

(_could_ because I don't know how mathematically hard that is)

~~~
duckerude
Here's one implementation:
[https://github.com/ipython/ipython/blob/be5c0545c3/IPython/c...](https://github.com/ipython/ipython/blob/be5c0545c3/IPython/core/ultratb.py#L444)

