[erlang-questions] tail recursion : sharing a learning and a question

Richard Carlsson richardc@REDACTED
Mon Aug 17 18:42:58 CEST 2009


Amit Murthy wrote:
> I just learned that every iteration of the below code goes onto the stack.
> The evaluation of the "catch" has something to do with it
> 
> do_maintenance(CacheSz, HKIntvl, Oldest) ->
>     try
>         receive
>             ...
>         after HKIntvl ->
>             ...
>             do_maintenance(CacheSz, HKIntvl, NOldest)
>         end
>     catch
>         error:Error ->
>             ...
>             do_maintenance(CacheSz, HKIntvl, Oldest)
>     end.
> 
> The right way of course is to not trap exceptions here, remove the try-catch
> and put the whole thing under a supervisor (with restart) behavior.
> Which is what I have done now.

It would be sufficient to rewrite it like this:

   do_maintenance(CacheSz, HKIntvl, Oldest) ->
       try
           receive
               ...,
               Oldest
           after HKIntvl ->
               ...,
               NOldest
           end
       of
         NewOldest -> do_maintenance(CacheSz, HKIntvl, NewOldest)
       catch
           error:Error ->
               ...
               do_maintenance(CacheSz, HKIntvl, Oldest)
       end.

The "protected" part of the code is the one between 'try' and 'of',
or between 'try' and 'catch' if there is no 'of' section. In the
protected part, tail recursive calls are not possible. (The call
in the 'catch...end' section, however, is tail recursive here.)

Basically, what your version of the code says is "if the recursive
call fails, we must catch that before we are allowed to exit from
the current call". The rewritten version above says "do the receive
and the log_info or cleanup, and catch any errors in that part (but
only in that part), then do a tail call to exit from the current call".

> Would still like to know as to why the try-catch ends up killing tail
> recursion. Can't the compiler catch this (pun intended) and optimize it for
> tail recursion?

No, because what the code says is that it should catch errors that may
happen in the recursive call, and handle the errors using the context of
the current call (e.g., the current value of Oldest). This means that
the compiler must keep the stack entry for the current call until the
recursive call has finished. Hence, no tail recursion optimization.

     /Richard


More information about the erlang-questions mailing list