26 October 2006

Ignoring tail call recursion...

I'm feeling bitchy right now, so if you don't want to here my venting on tail call recursion optimization, move along.

This bitch is specifically directed at the code in Bruce Tate's article on IBM's developerWorks site, Concurrent programming with Erlang, but the exact same code shows up all over the place.

The problem is, when folks are trying to explain functional programming they love to use the fibonacci and factorial algorithms to demonstrate how cleanly the mathematical definition translates into functional code. Invariably, you see something like this:

factorial(0) -> 1;
factorial(N) -> N * factorial(N-1).

And we all say, "wow, that's really cool how concisely you've written that function". But then we also say, "wait a minute, that recursive call is going to cause your stack to grow and grow, that ain't cool". And if you are thinking that, you're totally justified in doing so, because you're right. That really isn't cool. That's because the author, in an effort to introduce as few new concepts as possible, is leaving out the standard use of an accumulator to allow for tail call optimization.

Okay, if you don't already know what tail call optimization is, I'm sure you're dying to know now. It's really quite a simple concept, but is really important if you want to get the most out of your recursive functions. What tail call optimization does is it looks at the current stack frame where a function call is being made, and if that function call does not rely on anything outside of its own arguments, it releases the resources from that frame. This allows the garbage collector to come through and do its job, it also allows you to call a recursive function an infinite number of times without overflowing the stack. Let me show you what I mean with some code.

factorial(N) -> factorial(N, N-1).
factorial(Accum, 0) -> Accum;
factorial(Accum, N) -> factorial(Accum * N, N-1).

The function that clients would call is still the single argument version of factorial, but now we've introduced a new two argument (btw, in Erlang, the number of arguments a function takes is called its "arity") function. Looking at the two versions of the function, you'll see that instead of the recursive call "N * fact(N-1)" we take that N that's hanging out outside of the function and put it within the functions arguments. This one small change now allows the compiler/virtual machine/whoever to look at the last function called and see that there's nothing left in the current stack frame that will be used in the future, so that frame can be forgotten (gc'd). When we had that hanging reference to N in the first version of the function the stack frame could not be eliminated because down the line, when the recursive function returned, it would then need to use that value of N. You can test this out by executing the two versions of factorial, on my machine trying to evaluate factorial(100000) crashed the non-optimized version while the optimized version ran just fine (Erlang also handles numbers of arbitrary size, so factorial(100000) spits out a crazy big integer value).

Once you have tail call optimization you can start to see how functional languages can get away with not having for loops (a pure functional language can't have for loops because that would require incrementing a variable and variables can only be assigned once in FP). So, instead of saying:

for(int i = 0; i < 50; i++){ do_something(i); }

we would say:

for_loop(i, max) when (i < max) -> do_something(i), for_loop(i + 1, max);
for_loop(i, max) -> .//end

Here, for_loop is just a function that uses an accumulator and a max value. Since that last call to for_loop does not require any other information besides what's in its arguments, it the current stack frame will be removed. Without tail call optimization the iterative version of the for loop is clearly superior to the recursive version, but with optimization the two are basically equivalent.

11 October 2006

What I saw from Alvin

Well, this post has been a long time coming. Mainly because it's a daunting task to describe what diving in Alvin was like, so I kept putting it off. I've decided that a sub-par description is better than none at all, so here goes.

For three weeks in August I went to sea on the research vessel Atlantis with a team of fellow MBARI engineers as well as scientists from Dr. Ken Smith's lab. My team was there to test the autonomous vehicle we've been building, hopefully I'll talk more about that some other time. The special thing about Atlantis is that it is home to the deep sea research submarine Alvin, one of the few deep sea manned submersibles in the world. Because Ken Smith, the chief scientist of the cruise, is such a great guy, he tried his best to get as many people down in Alvin as the schedule permitted. Luckily, I was one of those lucky people.

Diving in Alvin was amazing. On my dive we went to 4100 meters deep and I seem to remember the rate of decent being around 25 meters a minute. So, that means for the first two hours and forty minutes we were descending. Not a minute of that time was boring though. There was a ton of stuff to see out of the small window next to where I was sitting, what I was struck by throughout the dive was how much biomass there actually is in the ocean. From about 250 meters deep until about 2000 meters there was a ton of bioluminesence. As the sub descended, it created turbulence in the water and this caused creatures affected by that turbulence to bioluminesce, a really amazing sight. When we eventually got to the bottom we quickly navigated to where our rover was parked and waited for it's mission script to begin. The evening before the dive I had been making code changes to the Rover, trying to work around a hardware bug that had only been uncovered after implementing some new behaviours. So, I was really interested (ahem, nervous) in how the Rover would perform. Luckily, the Rover did a pretty good job. It moved in the right direction, and at the right speeds (we were testing different speeds) right up until it got to the new moves we wanted it to make (turning to various compass points), then it made what right turn and quit. Great, nothing like seeing your bugs manifest themselves at 4100 meters. Luckily I was more worried about the 9000 pounds per square inch of water pressure we were under then what my colleagues would say about the bug, so I kept enjoying myself ;-)

So, with the Rover finished with it's mission we had other science objectives to complete. I'm not going to get into those, because this has already gone on longer than I'd planned. But the whole reason I wanted to make this post was to show the following video. We saw this little Isopod swimming by near the Rover and we were so intrigued by it that we followed it for several minutes. This kind of crazy creature is right at home in the deep ocean, where everything was a bit crazy.

Update: This isopod is from the family munnopsid and is a bathyopsurine, possibly a Bathopsurus or Paropsurus (see the comments section for how I found this out).



I felt like I was inside one of Salvador Dali's surreal landscapes...