
Recursion to Iteration, Part 4: The Trampoline (2013) - tempodox
http://blog.moertel.com/posts/2013-06-12-recursion-to-iteration-4-trampolines.html
======
kmill
One point of view of a trampoline is that you are implementing a small virtual
machine (or, in the terminology of SICP and I believe the original Scheme
paper, a "register machine") with two instructions: call and return. The
virtual machine provides the programmer with a very controlled goto statement,
known as a tail call.

The more general principal is that you can introduce small virtual machines to
extend a language with features not present in the original one. In an extreme
case, you can extend C with a virtual machine that implements all of Common
Lisp. Much less extreme is an asynchronous event loop using generators. (A
language isn't just its syntax.)

------
flavio81
This series make use of (or link to) this very interesting python "tutor" i
found really nice:

[http://www.pythontutor.com/visualize.html#mode=display](http://www.pythontutor.com/visualize.html#mode=display)

Separate topic: Comment of the first part of the series:

> _Alternative title: I wish Python had tail-call elimination._

I really like Python a lot and there are many features that could be added to
Python, but on the other hand, Python is just nice as it is, as long as what
you're trying to do fits into its capabilities. There's also the case of some
of those potential features threatening to go against the Zen of Python
(particularly "There should only be one way to do it" and "If the
implementation is hard to explain, it's a bad idea.")

If you are going to require high-performance * , or things that collide with
the Global Interpreter Lock, etc, then simply Python is not the best choice.
I'd also liked Python to have Tail Call Elimination, along with many other
features I would love Python to have, but for those I simply use Common Lisp,
which does have them (plus a ton of other features), however it will not
produce code as beginner-friendly as with Python.

* PyPy can only get you so far here.

~~~
weberc2
> There's also the case of some of those potential features threatening to go
> against the Zen of Python

It seems like "Zen of Python" is applied inconsistently, especially the "one
way to do it" rule. Python is horrid about "one way to do it"\--list
comprehensions vs loops, decorator syntax vs `x = decorate(x)`,
try/except/finally vs with, etc etc. In particular, if we are going to force a
choice between iterative and recursive based on "one way to do it", then I
would vote recursion + tco. Of course, this is a false dichotomy and we could
have the best of both worlds.

> if you are going to require high-performance * , or things that collide with
> the Global Interpreter Lock, etc, then simply Python is not the best choice.

Unfortunately, people rarely realize their performance requirements until
they're neck-deep in Python. Basically if you ever, ever, ever think you might
need decent performance, there are performant languages that beat Python at
its own friendliness/maintainability game (Go comes to mind).

~~~
scardine
I don't see a conflict between loops, list comprehensions and other shortcuts
- use the syntax sugar where it makes the code simpler/easier to read,
otherwise stick to plain loops.

My own rules for "list/dict comprehension" versus "loops" versus "generator
expressions" are the following:

* use a loop where you are interested only in the side-effects or where it will make the code easier to read

* use generator expressions where you don't really need to instantiate a list, for example when you are just iterating over the result and throwing it away.

* use list or dict comprehensions where you need to build and save a list/dict and the resulting code does not resemble a lisp program (where the choice does not make the program harder to read).

~~~
weberc2
I agree, but it's nevertheless a violation of the "one obvious way" rule.

------
tabtab
I used to use "visit" flags in relational projects to incrementally scan
trees. You start by putting the top node in a node inventory table, scan it,
and add any new nodes (branches) to the inventory table, with the "visit" flag
initialized with False. (Index the visit flag.) You keep querying and scanning
nodes until all the visit flags are True. Pseudo-Code:

1\. Add first node to inventory, set node.visit=false.

2\. Query for 1st of any nodes with visit=false. (Order doesn't really matter
in the end.)

3\. If none found, stop.

4\. If the found node has branches, add them to inventory with visit=false.

5\. Process current node.

6\. Mark current node with visit=true.

7\. Go to 2.

~~~
jessaustin
When you say "relational projects" and "query for", you don't mean that you
were using an RDBMS? Because, in that case, this pseudo-code represents a
_gigantic_ waste of time and resources.

------
halayli
There are plenty of bottlenecks in python. If you're really concerned about
performance, maybe python isn't the tool you're looking for.

~~~
afshinmeh
such as?

~~~
halayli
Gil?

The fact that almost everything is a PyObject and requires allocation?

~~~
nerdponx
It's not that bad if you have just a few hot paths. They can be written in
Cython or Numba without much trouble.

------
fnl
As usual, this kind of misses the point. Trampolining for TCO/TCE is fun, but
not really worth it. Rather it's cool for concurrency and scheduling, like in
Twisted. Dabeatz had a great tutorial in 2009 (using Py 2.5+ - oh my! :-)):
[http://www.dabeaz.com/coroutines/](http://www.dabeaz.com/coroutines/)

