Tail recursion in Erlang

I am really struggling to understand tail recursion in Erlang.

I have the following eunit test:

db_write_many_test() -> Db = db:new(), Db1 = db:write(francesco, london, Db), Db2 = db:write(lelle, stockholm, Db1), ?assertEqual([{lelle, stockholm},{francesco, london}], Db2).

And here is my implementation:

-module(db) . -include_lib("eunit/include/eunit.hrl"). -export([new/0,write/3]). new() -> []. write(Key, Value, Database) -> Record = {Key, Value}, [Record|append(Database)]. append([H|T]) -> [H|append(T)]; append([]) -> [].

Is my implementation tail recursive and if not, how can I make it so?

Thanks in advance


Your implementation is not tail recursive because append must hold onto the head of the list while computing the tail. In order for a function to be tail-recursive the return value must not rely on an value other than the what is returned from the function call.

you could rewrite it like so:

append(Acc, []) -> %% termination; Acc; append(Acc, [H|T]) -> Acc2 = Acc ++ dosomethingto(H); %% maybe you meant this to be your write function? append(Acc2, T); %% tail rercursive

Notice that all the work is finished once the tail recursive call occurs. So the append function can forget everthing in the function body and only needs to remember the values of the arguments it passes into the next call.

Also notice that I put the termination clause before the recursive clause. Erlang evaluates the clauses in order and since termination clauses are typically more specific the less specific recursive clauses will hide them thus preventing the function from ever returning, which is most likey not your desired behaviour.


  • AttributeError 'module' object has no attribute 'runserver' in django
  • Mock standard input - multi line in python 3
  • Django testing - Multiple queries when externally modifying the data. Cache Issue?
  • INFO_MSG method fails in ejabberd
  • IronPython/C# Float data comparison
  • Deploying pre-encrypted configuration files to a production environment
  • Instanciate service on startup in Angular2
  • get passwords from chrome
  • JSON - slashes not escaping
  • Less Conflicting Session Manager for Zope 2
  • Retaining data after updating application
  • Examples of how to a STS in .Net 4.5 using WCF
  • How to skip require in ruby?
  • How can I get the choice “H2” back in the H2 consol?
  • Getting unused unique values on a SQL table
  • abstracting over a collection
  • How to get data from **Realm database** using **date object**?
  • Do query loads all the data in memory
  • Can't remove headers after they are sent
  • C++ pointer value changes with static_cast
  • WPF ICommand CanExecute(): RaiseCanExecuteChanged() or automatic handling via DispatchTimer?
  • Eloquent paginate function in Slim 3 project using twig
  • SharedPreferences or SQLite Database?
  • Word Open XML Mail Merge
  • Meteor helpers not available in Angular template
  • Recording logins for password protected directories
  • Why value captured by reference in lambda is broken? [duplicate]
  • How to extract text from Word files using C#?
  • How to check if every primary key value is being referenced as foreign key in another table
  • Sending data from AppleScript to FileMaker records
  • Why is the timeout on a windows udp receive socket always 500ms longer than set by SO_RCVTIMEO?
  • Buffer size for converting unsigned long to string
  • using HTMLImports.whenReady not working in chrome
  • Hits per day in Google Big Query
  • Why joiner is not used after Sequence generator or Update statergy
  • How to get Windows thread pool to call class member function?
  • Recursive/Hierarchical Query Using Postgres
  • How to get NHibernate ISession to cache entity not retrieved by primary key
  • Observable and ngFor in Angular 2
  • UserPrincipal.Current returns apppool on IIS