HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
cjfrisz
clojure-tco
Clojure2012–2015
cjfrisz / clojure-tco

clojure-tco

Expanding support for constant-space tail calls in the Clojure language.

5Distinct people
5Mentions
1Explicit recommenders
173GitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

Expanding support for constant-space tail calls in the Clojure language.

GitHub

Where it is used

No documented product use has been verified yet.

What people are saying

>Unfortunately Clojure is one of the few lisps without proper tail calls. (I love Clojure I just thought I should point this out in the context of the discussion). Fortunately,…

chongli used a recommendation phrase. One indexed comment matches. This is a phrase match, not a verdict.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

No other repository is repeatedly linked in the same discussions.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 3:56:07 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year5 dated mentions

Observed mentions: 2012-05-11 18:52:33 UTC to 2015-02-08 05:11:57 UTC.

0122012-01-01 00:00:00 UTC to 2013-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments20122013-01-01 00:00:00 UTC to 2014-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments20132014-01-01 00:00:00 UTC to 2015-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2015-01-01 00:00:00 UTC to 2016-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2015

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20122012-01-01 00:00:00 UTC2013-01-01 00:00:00 UTC202
20132013-01-01 00:00:00 UTC2014-01-01 00:00:00 UTC202
20142014-01-01 00:00:00 UTC2015-01-01 00:00:00 UTC000
20152015-01-01 00:00:00 UTC2016-01-01 00:00:00 UTC101

What Hacker News says

5 sources
I did a little homework before posting the comment you're replying to, and found a few sources stating that Clojure lacks general tail call optimization due to limitations in the JVM. I'll share what I dug up with you, although to be honest I'm almost just making an appeal to authority (actually to several authorities!), because I haven't actually read the JVM spec. Rich Hickey wrote: "No language that uses the JVM stack and calling convention (e.g. Clojure and Scala) can do TCO since it would require either stack manipulation capabilities not offered in the bytecode spec or direct support in the JVM. The latter is the best hope for functional languages on the JVM, but I'm not sure is a priority for Sun as tail-calls are not idiomatic for JRuby/Jython/ Groovy." [0] I thought that there are implementations of Scheme and other languages on
Read the rest
the JVM that do tail call optimization, but I assumed they utilised some kind of virtual stack. Jörg W Mittag on StackOverflow confirmed this: "Nah, TCO [on the JVM] is easy. Seph does it, Erjang does it, Kawa and all the other Scheme implementations on the JVM do it. The JVM has Exceptions, which are basically the same as GOTO, which can be used to implement TCO. Or you use trampolines. Or you don't use the JVM call stack at all and just implement your own. The reason why Clojure and Scala only provide limited TCO (basically, only tail recursion is optimized) is because they want to use the JVM call stack for interoperability and performance reasons. As Rich Hickey, Clojure's designer said: Interop, speed, TCO -- Pick two." [1] James Iry wrote the following on LtU, which explains why even though "Real instruction sets (or C) don't 'support' proper tail recursion either", the fact that the JVM doesn't is an issue for Clojure (and Scala): "Native instruction sets often let you do whatever you want with the stack. C doesn't in the ANSI standard, but you can do it with a bit of assembly. .NET IL has an explicit instruction for tail calls. The JVM, on the other hand, is very strict about how you use its stack and has no tail call instruction." [2] During my Googling, I also found this[3], which is pretty interesting! "CTCO works by applying a first-order one-pass CPS algorithm (via Danvy 2007), then transforming the code to return thunks, and finally creating a custom trampoline to be used when the code is executed. Thanks to the properties of the CPS transformation, CTCO will make all function calls into tail calls, thereby even making non-tail code compiled by CTCO use constant space." This actually sounds not far from what you suggested! Although it does seem to be a bit more than "just an AST transformation". I haven't read through it, or read the referenced paper [4] it's a bit over my head. You should check it out and report back! I've bookmarked it for now. Finally, you wrote: "And I don't think it's a trade-off in the least. If so, what are Clojure programmers winning now? The glorious overhead of thousands of function calls? Those tasty 'stack overflow' back-traces, with thousands of calls to the same function?" Do you still think it's not a trade-off? Unless that last link I found is a magic bullet that makes TCO fast without interfering with Java interop (or the performance of Java interop) then I really don't think it's fair to say that it's not a design trade-off. Rich Hickey (and Martin Odersky) obviously made a conscious decision about this, and you're welcome to disagree with them, but it's not clear cut. What Clojure programmers are winning now (by being hosted on the JVM) is a lot, I think. Easy interop is one of the pillars of Clojure that has contributed heavily to it picking up steam. I think the same applies to Scala. I said earlier that I don't have an opinion, and I guess it's only true that I don't have a strong opinion. If I had to pick, I think I'd pick interop. Sorry for the long comment but I'd already done the research, I figured I might as well share it, although there really already has been a great deal said about this. [0] https://groups.google.com/forum/?fromgroups=#!msg/clojure/Oi... [1] http://stackoverflow.com/questions/7261039/haskell-on-jvm [2] http://lambda-the-ultimate.org/node/3106 [3] https://github.com/cjfrisz/clojure-tco [4] http://www.brics.dk/RS/07/Abs/BRICS-RS-07-Abs/BRICS-RS-07-Ab...
In The Mathematical Hacker
I've spent a number of months working on using CPS and trampolining to add proper tail calls to Clojure. I have a video presenting my work here: http://www.chrisfrisz.com/blog/?p=220 . You can also take a look at the code here: https://github.com/cjfrisz/clojure-tco . The punchline is that there are several things you can do to improve your performance: first, a number of more efficient CPS algorithms have been developed over the last 10+ years. I currently use one presented by Danvy in the 2001 paper "A First-Order One-Pass CPS algorithm" linked here: www.brics.dk/RS/01/49/BRICS-RS-01-49.pdf . It's major focus is to distinguish between "serious" and "trivial" expressions, because the serious ones are the only ones that actually require a new continuation to be generated. This saves a lot of administrative redexes that can kill your perfo
Read the rest
rmance. Some other CPS-related avenues you may want to look into include "The Essence of Compiling with Continuations" (co-authored by one of my heroes, Amr Sabry) which formalizes A-normal form, an alternative to compiling with continuations that was ground-breaking in pointing out the number of unnecessary redexes introduced by traditional CPS algorithms. There's also a more recent paper by Andrew Kennedy at MS Research called "Compiling with Continuations, Continued" which I admittedly have not gotten a chance to dig into yet. It's stated goal is to revive some of the ideas of the original "Compiling with Continuations" by Andrew Appel. Of course with the method you present, continuation application isn't your only performance hit. If you're using a trampoline, you're undoubtedly thunkifying the code to delimit the bounces on the trampoline. I also need to do some work in this area, but I'm familiar with at least one paper on doing minimal thunkification from 1993 and linked here: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.158.... . I'm a big fan of both CPS+trampolining and the work like ClojureScript that's bringing efficient Lisp programming to the JavaScript arena. I'm hoping to get a chance to look over the work that you've done with Outlet and I'd love to chat with you about it. It sounds like we share quite similar interests.
In Compiling To Javascript In Continuation Passing Style, Can We Optimize?

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.