
Trampolines in JavaScript - austengary
http://raganwald.com/2013/03/28/trampolines-in-javascript.html?utm_content=bufferdab4c&utm_source=buffer&utm_medium=twitter&utm_campaign=Buffer
======
crazygringo
Genuine question, and this seems to be as good a place as any to ask it. I
understand the concepts behind functional programming, and trying to implement
them in JavaScript (recently read _Functional JavaScript_ too).

But from my experience, functional programming is mainly useful for
parallelization/distribution of computation, because it's essentially
mathematical -- functional code tends to be harder to write, understand, and
debug, but you do it when you need to be able to split up computation. Kind of
like assembly is harder than C++, but you do it when you need 100% optimized
computation in a section of your program.

But because there is obviously no multithreading in JavaScript, I've never
found a compelling reason to use the FP paradigm in JavaScript. Can anyone
enlighten me as to real-world situations where FP makes sense in JavaScript,
either client- or server-side? Particularly in web-based projects, which is
obviously what JS is for?

~~~
sluukkonen
Small steps. Just using some simple functional constructs like `map` and
`filter` will do wonders for your code. You don't have to start writing
everything in a purely functional manner to benefit from it.

~~~
crazygringo
Oh, for sure -- I use map/filter/reduce/etc. whenever I can when they result
in cleaner/shorter code. They're great.

I'm asking more about things like tail-call recursion/trampolining, function
decoration -- making the "architecture" of your program more functional,
rather than just a few lines here or there.

~~~
cgag
Recursion isn't really hard once you get used to it, and often leads to
shorter/cleaner code. Map/filter/reduce are themselves very clearly expressed
using recursion.

------
imurray
_Did you know that “bookkeeping” is the only word in the English language
containing three consecutive letter pairs? You’re welcome._

Huh. Seems that's pretty much true. Occasionally people don't hyphenate
_sweettoothed_.

    
    
        grep '\(.\)\1\(.\)\2\(.\)\3' word_list

~~~
ArbitraryLimits
I always thought the Encyclopedia Brown case whose solution hinged on the
villain's not knowing that was far-fetched.

[http://brownencyclopedia.tumblr.com/post/33647027942/the-
cas...](http://brownencyclopedia.tumblr.com/post/33647027942/the-case-of-the-
hard-luck-boy)

------
bsaul
Very interesting article. But then, frankly, if you're ever in the need for
that kind of things, why don't you make yourself a favor and stop using
javascript (especially on the server, where there are plenty other
alternatives) ?

I mean, if someone comes to me with some server code running a "trampoline"
library so that he can perform tail-call optimization in javascript on node.js
, he really better have some extremely good justification.

~~~
tracker1
definitely true... If you're doing CPU intensive stuff in node, you should
consider the following... worker script(s) (generic-pool helps too), with
.fork(), refactoring the loop to use an async response with setImmediate, or
converting to a binary module.

Example: [https://github.com/tracker1/node-js-
scrypt/blob/master/lib/s...](https://github.com/tracker1/node-js-
scrypt/blob/master/lib/scrypt-async.js)

------
taylodl
Trampolines are okay, but JavaScript still needs an actual TCO. In this blog
post [http://taylodl.wordpress.com/2013/06/07/functional-
javascrip...](http://taylodl.wordpress.com/2013/06/07/functional-javascript-
tail-call-optimization-and-trampolines/) I look into the performance of
trampolines compared to the normal recursive form. Trampolines perform worse.
Moreover, TCO was invented to overcome the performance issues associated with
recursive implementations, meaning trampolines perform much worse than TCO.

------
jheriko
i've never been much of a fan of this 'fancy new terminology' and generally
learn it as needed...

however, there are two things i find scary here - and really both are the same
thing

\- the idea that a stack frame is something that is not known about \- that
solving the endless stack frame problem requires tail-call elimination or any
other specific optimisation that leaves the code in recursive form

this reminds me of this section in the Michael Abrash programming black book -
[http://www.phatcode.net/res/224/files/html/ch59/59-04.html#H...](http://www.phatcode.net/res/224/files/html/ch59/59-04.html#Heading8)

i was shocked at that as well tbh. unrolling that particular recursion by
understanding how functions work is something i just did once without any
special thought many years before encountering this book... i assumed that
programmers generally understand how to implement everything they use, or at
least have a deep curiosity about that - but this is a flawed assumption.

now, re writing recursive code to be iterative is something that i know from
experience that programmers will shy away from until they get to grips with
it. like many tasks it turns out that its both quick and easy and infact
always possible, however before learning this they will worry it will be
difficult or will take days. its such an easy task a dumb old computer can do
it when it compiles your code after all... a human brain should have no
problem (!)

if you implement your own stack based iterative method the equivalent to tail-
call optimisation falls out naturally without needing to change the inner loop
whatsoever (you literally end up writing code like c=a a=b c=a and realise you
can omit a) the result is nothing nearly as complicated or expensive as
trampolining - although it is a bit unreadable by my standards - implementing
trampolining as this article suggests is just as messy or worse.

------
erjiang
Here is an example of trampolining in a Scheme interpreter I wrote in
JavaScript:
[https://github.com/erjiang/foxscheme/blob/master/src/system/...](https://github.com/erjiang/foxscheme/blob/master/src/system/interpreter.js#L241)

This way, interesting Scheme programs can be written without using the small
JavaScript stack.

It uses an infinite loop that's broken out of by throwing an exception. There
are also some other tricks that it uses to allow other things to happen on the
page while it runs your program.

~~~
tcdowney
Looks like Friedman taught you well. :)

------
anonymoushn
Creating a new function for every tail call seems a bit awful. It's a shame
that the more performant alternatives are ridiculously ugly.

~~~
badman_ting
Right, plus how is someone supposed to come in, look at the resulting code,
and figure out how it works? Functions wrapped in functions wrapped in
functions. (Remember, this is supposed to be the "ridiculously simple" case.)

On the other hand, we do a lot of wrapping things in functions in JS-land, so
this probably says more about the state of the art than anything.

~~~
justincormack
A trampolined state machine reads OK, where each state is the next transition
function.

But generally, you want tail recursion...

------
numlocked
Hopefully this will be a bit less painful if/when TCO makes its way into
ECMAScipt 6 ("Harmony") as it is slated to do:
[http://wiki.ecmascript.org/doku.php?id=harmony:proper_tail_c...](http://wiki.ecmascript.org/doku.php?id=harmony:proper_tail_calls)

Regardless, this was an excellent introduction to trampolining.

------
RyanMcGreal
Sidenote: when you run the recursive function factorial(1000), Node.js returns
"Infinity".

~~~
dmm
Because javascript has only one numerical type: double float?

~~~
wging
> fact(1000) Infinity > fact(10000000) RangeError: Maximum call stack size
> exceeded

------
misterdata
This works even better with ES6 generators:
[https://github.com/pixelspark/dispatch.js](https://github.com/pixelspark/dispatch.js)

~~~
teamonkey
Doesn't ES6 have tail calls anyway?

------
nonchalance

        we need space O<em>n</em> for all those stack frames
    

Why not use the normal O(n) notation?

------
mhewett
You don't seem to be gaining much from this, at least in JavaScript. The Jatha
small LISP library that hasn't been updated since 2007 returns
factorial(32768) in 5 seconds on a middling machine. (Disclosure: I'm the
author of Jatha)

------
PotatoPotato
jQuery script to fix that whole article:
$("body").html($("body").html().replace(/rampol/g, "rambapol"));

Happy Friday Everyone!

------
IgorPartola
Very interesting and a nice hack. However, since you are already using a while
loop, you might as well do this:

    
    
      function factorial(n) {
          for (var res = 1; n > 0; n--) {
              res *= n;
          }
    
          return res;
      }
    

Simple, shorter, no overhead from function calls, etc.

~~~
raganwald
You know, this is the canonical programmer missing the forest by arguing the
shape of the leaf on a tree. The point is to discuss the mechanism, and
factorial happens to be a ridiculously simple example.

Perhaps what the article needs is a disclaimer such as:

 _We can easily rewrite this in iterative style, but there are other functions
that aren’t so amenable to rewriting and using a simple example allows us to
concentrate on the mechanism rather than the “domain.”_

;-)

~~~
IgorPartola
Exactly. My point is that the factorial function is always used as an example
when discussing tail call optimization and TCO is useless for it. I get how
this is useful. I just would love to see an example of actually using it for
something other than calculating factorials.

~~~
jfb
I understand this sentiment, but what simple zero-context function would you
choose in place of factorial? You'd want something that's trivially and
naturally recursive that doesn't also act on non-primitive types (to avoid
having to do a bunch of blahblah introducing the particulars of a given data
structure).

~~~
IgorPartola
How about merge sort or quicksort? Or maybe a binary search? Seems a little
more real-worldy. I suppose factorial is a good starting point to illustrate
how to construct the function, but an example or two of an actual use case
would be so so great.

~~~
jerf
"merge sort or quicksort? Or maybe a binary search?"

OK, but couldn't you then _still_ complain that each of those are "Simple,
shorter, no overhead from function calls, etc." as while loops?

~~~
IgorPartola
You might. The problem with doing a binary search in a while loop is that now
you have to actually maintain your own stack, which to me seems like it should
be left to the runtime. The point of using a recursion is that you can do the
same thing as a loop but with less code.

Mostly, the problem is that no developer in the last 10 years has ever had to
write an efficient factorial function except in a job interview or for a blog
post. That is slightly less the case with sorts and searches.

