[HN Gopher] Tail Recursion and Debugging (2011) ___________________________________________________________________ Tail Recursion and Debugging (2011) Author : lelf Score : 53 points Date : 2020-09-19 22:22 UTC (2 days ago) (HTM) web link (funcall.blogspot.com) (TXT) w3m dump (funcall.blogspot.com) | pizlonator wrote: | 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. | bachmeier wrote: | > 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. | gumby wrote: | 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) | rjmunro wrote: | 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. | mannykannot wrote: | Indeed; to debug either a loop or a recursive function (and | whether the latter is using tail-call recursion optimization or | not) you often need to see the data. Given a stack trace | showing only calls without arguments, about the only extra | datum you get with non-optimized recursion is how deep it went. | | In terms of debugging, tail-call elimination is no worse than | the optimizations typically applied to loops. | ufo wrote: | I think this might be what the author was trying to say with | that inscrutable stack trace that they weaved between the | various quotes. It would be reasonable to argue that all those | FilterDefinition.doFilter are making it harder to debug the | stack trace, not easier. | nradov wrote: | Some platforms such as the JVM allow for recording debuggers | which can keep a record of all opcodes executed. So developers | can play back a recording and step forward and back in time to | observe state changes. | braythwayt wrote: | 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 wrote: | What information should be logged? And if the loop iterates | for a million times, should we log it every time? | Jtsummers wrote: | 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. | heavenlyblue wrote: | how hard is it to log a million messages? | braythwayt wrote: | I guess you're suggesting that adding a million lines to a | log is excessive. But in my math, you can count lines like | this: 1, 2, 3, ..., the-number-of-lines-that-fit-in-a- | window, many. | | Once you have more lines than fit in a window, you've | already got "many" and now it's time to talk about tooling | like searching logs. | | A million lines of log feels like an excessive waste | intuitively, but that's only because I was brought up on | 8-bit computers with 16K of RAM. Now my watch has more | power than probably all of the computers I owned in the | 20th century put together, and a single image it displays | might have more bits than a million lines of a log file. | | --- | | I know this stuff isn't free, but we should at least | consider the possibility that what we really need are | proper and comprehensive logs/traces, and a "stack trace" | is nothing more than an abbreviated execution trace that | just happens to be relatively cheap to collect when | something goes wrong. | agapon wrote: | It's not the end of the world, of course, but sometimes it | makes debugging much harder. Especially with higher | optimization levels. | thamer wrote: | 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... | grantjpowell wrote: | 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 wrote: | The trace isn't repeating. Those are individual call stack | records. It is just printing the trace as it is. | CyberDildonics wrote: | They didn't say the trace was repeating. | thewisenerd wrote: | 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... | thewisenerd wrote: | 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 wrote: | Here's one implementation: | https://github.com/ipython/ipython/blob/be5c0545c3/IPython/c... | nonbirithm wrote: | 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. ___________________________________________________________________ (page generated 2020-09-21 23:02 UTC)