the blog for developers

This Function is Not Tail Recursive

Tail recursion seems to be an easy concept, but most people get it wrong – including me. Reading the latest German Java SPEKTRUM, I’ve found an article about parallel multicore development by Kornelius Fuhrer. One paragraph was about functional development and tail recursion. First he claims tail recursion makes functions 100% parallizeable (I guess broadly speaking all compositions h(g,f) of side effect free functions g,f are 100% parallizeable in f and g, nothing to do with tail recursion) then he claims his example functions are tail recursive:

Wikipedia says about tail recursion:

In computer science, tail recursion (or tail-end recursion) is a special case of recursion in which any last operation performed by the function is a recursive call, the tail call, or returns a (usually simple) value without recursion.

In all three examples the last operation performed is the multiplication (*), not the function call to itself. First the function itself is called, then the return value is multiplied by n. Stackoverflow has a lot to say about tail recursion too.

A tail recursive version of factorial might look like this:

So please, do not call all functions where the last function call in your source code is a call to itself, tail recursive. A function is tail recursive, if the last operation is a function call to itself.

You can leave a Reply here. Of course, you should follow me on twitter here.

You can share this post!
Do you want to tell others about this article? Use the social bookmark icons to submit this artice to the service of your choice. Thanks.

About the author: Stephan Schmidt has more than 15 years of internet technology experience and 10 years experience in agile. He was head of development, consultant and CTO and is a speaker, author and blog writer. He specializes in organizing and optimizing software development helping companies by increasing productivity with lean software development and agile methodologies. Want to know more? All views are only his own.

24 Tweets 48 Comments

Leave a reply.

Comments

Nick Wiedenbrueck

Haven’t read the article, but that would be quite embarrassing.

Anyway, just wanted to add, that tail recursion generally is more efficient than non-tail recursive functions, because ressources don’t have to be maintained over the recursive calls – at least in theory. On the JVM there will be created a new stack frame on each call, because tail recursion is not supported very well. On the other hand, the Scala compiler has tail call optimization and basically would turn the calls into a while loop.

Read my blog post on this topic, if you like http://stronglytypedblog.blogspot.com/2009/08/scala-tail-recursion.html

@Nick: Thx for the comment – left it out of the article because I’ve blogged about that in the linked post.

Probably it’s better to have the information here too – so thanks.

Perhaps a better way to think of it is that a function is tail recursive if its return value is just the return value of the recursive call. Trying to figure out what the last operation is can be slightly unintuitive, but figuring out that the value of the recursive call gets passed straight through to the original function’s caller is slightly easier to grasp.

Perhaps Scala @tailrec should also be mentioned.

Tail recursive functions are not necessarily parallelizeable- consider fold_left (aka reduce, for you lispers). It’s easier to determine if pure functions can be parallelized or not, as the dependencies are explicitly stated as data dependencies. But if a function f uses as an input the output of function g, then f and g are (generally) not parallelizable.

@Brian: Sorry, I did not write what I’ve meant.

I’ve meant h(f,g) then f and g are parallelizeable.

dude

Tail recursion is a compiler optimization, so what matters isn’t whether or not the last call is a simply function call or a function call as part of a bigger expression, but rather if the compiler can optimize a tail recursive call out of the code _period_.

Scala specifically, due to limitations of the JVM, cannot optimize away the example code you provided — very true. And it’s almost certainly better to write it the second way to be sure.

Just wanted to point out that it’s the compilers job to make it tail recursive in the first place, and so it’s ability to do that will vary by language.

One of the nice things about lisp-like languages (or any non-infix one, really) is that the mistake would’ve been obvious from the beginning since instead of “n * fac(n -1)” it would be written “(* n (fac (- n 1)))” and you would clearly see that there’s another function outside fac.

Paul Phillips

Not only is there @tailrec, but these days it’s very specific about how you blew it. Since there’s no preview and I have no idea what markup options I have this will probably be a mess, but:


scala> import annotation._
import annotation._

scala> @tailrec def fac(n: Int): Int = if (n==0) 1 else n * fac(n-1)
:8: error: could not optimize @tailrec annotated method: it contains a recursive call not in tail position
@tailrec def fac(n: Int): Int = if (n==0) 1 else n * fac(n-1)
^

@Paul: Thanks for this example, haven’t tested @tailrec myself.

@Dude: Tail recursion has nothing to do with compilers. Compilers can use the fact that a function is tail recursive and optimize. But the connection is not the other way round.

dude

@stephan: I think you just misunderstood what I said and then just rephrased my point :)

@dude: Might be, but I don’t think so :-)

Your comment sounds like it depends on a compiler if a function has tail recursiveness. It doesn’t.

Douglas

@stephan: it is certainly possible to write a compiler/interpreter which does not perform tail call optimisation on any of your examples. One reason to do so might be to output full stack traces, including those frames which could be discarded through tail call optimisation to aid debugging. So, whether a tail call is made is very much influenced by the compiler.

However, the ease with which a function may be optimised is, as you say, a property of the function itself.

@Douglas: Or because tail-recursion is slower – see

http://debasishg.blogspot.com/2008/10/to-tail-recurse-or-not.html

Paul Phillips

@stephan: He’s demonstrating it was slower than iterating over a mutable structure, not that it’s slower than unoptimized recursion. This leaves you with no apparent point. I can’t think of any scenario in scala where transforming a tail call into a jump could slow it down compared to not doing so.

@Paul: If I need to decided between tail recursion and local mutability, it’s not automatic that tail recursion ist better. Like tail recursion being a hammer to solve all problems because it’s fast.

littleli

There is also a great post from John Rose about tail calls support in JVM http://blogs.sun.com/jrose/entry/tail_calls_in_the_vm and I hope there is something on the way to Java 7 that is going to be useful.

@littleli: Funny, first comment on that link says

Interesting stuff John. It would be interesting to see some examples that would benefit from such optimization. Am I correct that the following code could use this feature to achieve better performance? int f(int i) { return i==1 ? 1 : i + f(i-1); } Do you think if javac compiler could such optimizations automatically for the regular Java language.

Q.E.D.

Stefan Plantikow

In practice I live by the following definition: It’s tail recursive if there is no return point in the function that requires the current stack frame to be kept around until the recursive call returns. Beyond this one only needs to know if one’s compiler is clever enough to actually notice this and do something smart about it (like turning the tail recursive function application into a loop).

Oh, and it has about nothing to do with being parallelizable except that tail-recursive functions often tend to be referentially transparent, too, and therefore are memoizable (can be cached).

Leave a Reply

What people wrote somewhere else:

Blogged: “This Function is Not Tail Recursive” http://bit.ly/9Sg3LB

This comment was originally posted on Twitter

RT @codemonkeyism: Blogged: “This Function is Not Tail Recursive” http://bit.ly/9Sg3LB

This comment was originally posted on Twitter

RT @codemonkeyism: Blogged: “This Function is Not Tail Recursive” http://bit.ly/9Sg3LB

This comment was originally posted on Twitter

Stephan Schmidt: This Function is Not Tail Recursive http://bit.ly/aEuB90

This comment was originally posted on Twitter

RT @codemonkeyism: Blogged: “This Function is Not Tail Recursive” http://bit.ly/9Sg3LB

This comment was originally posted on Twitter

RT @codemonkeyism: Blogged: “This Function is Not Tail Recursive” http://bit.ly/9Sg3LB #dev

This comment was originally posted on Twitter

This Function is Not Tail Recursive – Comments http://ow.ly/18ckZE

This comment was originally posted on Twitter

This Function is Not Tail Recursive http://bit.ly/981moH

This comment was originally posted on Twitter

“Code Monkeyism: This Function is Not Tail Recursive” ( http://bit.ly/cpuZsP )

This comment was originally posted on Twitter

This Function is Not Tail Recursive http://bit.ly/9Sg3LB

This comment was originally posted on Twitter

RT @visibletrap: This Function is Not Tail Recursive http://bit.ly/9Sg3LB // Every tail recursion has an “accumulator” as a data-pipe.

This comment was originally posted on Twitter

HNews: This Function is Not Tail Recursive http://bit.ly/aRuCTe

This comment was originally posted on Twitter

This Function is Not Tail Recursive http://bit.ly/bIHjOQ (http://bit.ly/9xke97) via @fogus

This comment was originally posted on Twitter

RT @codemonkeyism: Blogged: “This Function is Not Tail Recursive” http://bit.ly/9Sg3LB #scala

This comment was originally posted on Twitter

This Function is Not Tail Recursive http://bit.ly/amqFth

This comment was originally posted on Twitter

entenda tail recursive functions; http://codemonkeyism.com/function-tail-recursive/

This comment was originally posted on Twitter

RT @PlanetScala: Stephan Schmidt: This Function is Not Tail Recursive http://bit.ly/aEuB90

This comment was originally posted on Twitter

RT @codemonkeyism This Function is Not Tail Recursive http://bit.ly/9Sg3LB

This comment was originally posted on Twitter

RT @codemonkeyism Code Monkeyism: This Function is Not Tail Recursive http://bit.ly/9Sg3LB

This comment was originally posted on Twitter

This Function is Not Tail Recursive http://ff.im/-nWYJB

This comment was originally posted on Twitter

This Function is Not Tail Recursive http://bit.ly/cRUIIA

This comment was originally posted on Twitter

#programming Interesting read: This Function is Not Tail Recursive – http://su.pr/5O73Nf

This comment was originally posted on Twitter

Interesting read: This Function is Not Tail Recursive – http://is.gd/dHZDq #programming

This comment was originally posted on Twitter

This Function is Not Tail Recursive http://bit.ly/btwg75

This comment was originally posted on Twitter

Additional comments powered by BackType

Guide to CodeMonkeyism

Over the last 4 years I wrote many articles on this blog. To make it easier for you to find the relevant ones, I've organized them into topics.

Top 10

6 reasons why my VC funded startup did fail

Go Ahead: Next Generation Java Programming Style

Java Interview questions: Write a String Reverser

The dark side of NoSQL

7 Bad Signs not to Work for a Software Company or Startup

Is Java dead?

Scala vs. Clojure

Never, never, never use String in Java

No future for functional programming in 2008 – Scala, F# and Nu

Clojure vs Scala, Part 2

Java Developer

Is Java Dead?

Go Ahead: Next Generation Java Programming Style

Be careful with magical code

All variables in Java must be final

Never, never, never use String in Java

Bending Java: More readable code with methods that do nothing?

NoSQL Guy

NoSQL: The Dawn of Polyglot Persistence

The dark side of NoSQL

Essential storage tradeoff: Simple Reads vs. Simple Writes

Sharding destroys the goals of your relational database

The unholy legacy of databases

Startup/CTO

Development Dream Teams

6 reasons why my VC funded startup did fail

American vs. European style of Software Development

12 Things to Reduce Your Lead Time and Time to Market

The high cost of overhead when working in parallel

Essential storage tradeoff: Simple Reads vs. Simple Writes

Job Seeker

Another Good (Java) Interview Question

7 Bad Signs not to Work for a Software Company or Startup

Java Interview questions: Write a String Reverser (and use Recursion!)

Java Interview questions: Multiple Inheritance

As a Manager: What I value in developers

Top 10 Tips (+1) to Get a Pay Raise

Agilist

What Developers Need to Know About Agile

5 Practices Better to Change in Your Scrum Implementation

Scrum is not about engineering practices

ScrumMaster and ZenMaster: The joke of certification

What is Trans-Scrum?