Is there a way to enable Tail Recursion Optimization in TypeScript?

const isPositive = (n: number) => n > 0;

function fitsIn(dividend: number,
                divisor: number,
                count: number,
                accum: number): number {
  if (accum + divisor > dividend) {
    return count;
  }
  return fitsIn(dividend, divisor, count + 1, accum + divisor);
}

function divide(dividend: number, divisor: number): number {
  let timesFits = fitsIn(Math.abs(dividend), Math.abs(divisor), 0, 0);
  return isPositive(dividend) === isPositive(divisor) ? timesFits : -timesFits;
}

console.log(divide(10, 3));
// 3

console.log(divide(-2147483648, -1));
// RangeError: Maximum call stack size exceeded

console.log(divide(10000, 1));
// RangeError: Maximum call stack size exceeded

I attempted to execute this code using TypeScript 4.6.2 in Strict Mode and encountered a stack overflow error. The issue seems to be related to the recursive nature of the function combined with accumulation within each recursive call. Is there a way to optimize this code for tail recursion? What modifications are needed to achieve tail recursion optimization?

Answer №1

When it comes to TypeScript, optimizing tail-called recursive functions is not on the cards. For more information, refer to microsoft/TypeScript#32743.

In JavaScript, 'tail call optimization' typically involves converting a tail-recursive function into an iterative version. This differs slightly from 'tail call elimination,' where recursion is maintained but the current stack frame is rewritten instead of creating a new one. While both methods appear similar in terms of stack growth, optimization generally operates at a higher level than elimination.


If you are considering suggesting that TypeScript should incorporate 'tail call optimization' for performance gains, it goes against TypeScript's design principles. The compiler aims to emit your code as it is written, without optimizing or altering it. Therefore, automatic optimization is not within TypeScript's scope.


Alternatively, you might be referring to 'proper tail call elimination' introduced in ECMAScript 2015 (ES6). Although this feature was intended to prevent adding to the call stack with tail calls, its adoption has been limited, primarily observed in Safari-based browsers. Runtime engine developers encountered challenges like potential performance issues and user confusion when implementing this feature, leading many engines to take a cautious approach. As of now, there is no concrete proposal for explicit syntax to enable such eliminations.

While it is technically feasible for TypeScript to downlevel proper tail call elimination into a form of tail call optimization, similar limitations would likely arise, prompting the team to refrain from pursuing it.


In conclusion, automatic tail call elimination seems to be inactive in the current landscape, with TypeScript showing no intentions to revive this feature.

Answer №2

Check this out - it might be exactly what you're looking for:

function Tailcall <T> = { head: T, tail: () => Tailcall<T>, done: boolean } ;

const Tailcall = 
(done: boolean) => 
<T,> (head: T) => 
(tail: () => Tailcall<T>)
: Tailcall<T> => 
    
    ({ head: head, tail: tail, done: done }) as Tailcall <T> ;

Tailcall.done = 
<T,> (head: T)
: Tailcall<T> => 
    
    Tailcall (true) (head) 
        (() => { throw new Error("not implemented"); }) ;

Tailcall.call = 
<T,> (tail: () => Tailcall<T>)
: Tailcall<T> => 
    
    Tailcall (false) (null as any) (tail) ;

Tailcall.RUN = 
<T,> (self: Tailcall<T>)
: T => 
{
    let RUNNING = self;
    while (!RUNNING.done) 
    { RUNNING = RUNNING.tail (); }
    return RUNNING.head ;
} ;


const rem = 
(n: number, r: number)
: Tailcall<number> =>
    
    (n < r) ? Tailcall.done(n) 
    : Tailcall.call(() => rem(n-r, r)) ;


const pipeline = <T,> (x: T) => <R,> (f: (x: T) => R) => pipeline((f) (x)) ;

pipeline (rem(10000002,2))
    (Tailcall.RUN)
    (console.log); // 0, no stack overflow here

Alternatively, you can also explore this classic-style implementation:

class TailCall
<T> 
{
    constructor
    (
        public readonly isComplete: boolean ,
        public readonly result: T ,
        public readonly nextCall: () => TailCall<T> ,
    ) {} ;
    
    static done
    <T>(value: T)
    : TailCall<T> 
    {
        return new TailCall(true, value, () => { throw new Error("not implemented"); }) ;
    } ;
    
    static call
    <T>(nextCall: () => TailCall<T>)
    : TailCall<T> 
    {
        return new TailCall(false, null as any, nextCall);
    } ;
    
    invoke(): T 
    {
        let tailcall: TailCall<T> = this ;
        while (!tailcall.isComplete) 
        { tailcall = tailcall.nextCall() ; } ;
        return tailcall.result ;
    } ;
} ;

const rem = 
(n: number, r: number)
: TailCall<number> =>
    
    (n < r) ? TailCall.done(n) 
    : TailCall.call(() => rem(n-r, r)) ;

console.log(rem(10000001,2).invoke()); // output: 1

These implementations are based on tail recursion concepts discussed in this Java blog.

The code is sourced from pure.ts.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"I'm encountering an issue with the discord.js module when I try to launch my bot using node. Any ideas on how

I encountered an unusual error with my Discord bot recently. It seems that discord.js crashes every time I try to run my bot: [nodemon] 2.0.12 [nodemon] to restart at any time, enter `rs` [nodemon] watching path(s): *.* [nodemon] watching extensions: js,mj ...

The functionality of jQuery binding is not functioning properly

I've been playing around with jQuery and have a code snippet that includes: // buttons for modifying div styles by adding/removing classes <button class="size" id="switcher-large"> Large Print </button> <button class="size" id="switche ...

Accessing JSON data stored locally and initializing it into a TypeScript variable within a React application

I'm new to working with JSON arrays and I'm facing a challenge. I am looking for a way to load data from a JSON file into a Typescript variable so that I can perform a specific operation that involves arrays. However, I'm unsure of how to ac ...

Challenges arising with the express search feature

Currently, I am in the process of developing an API for a to-do application. I have successfully implemented the four basic functions required, but I am facing some challenges with integrating a search function. Initially, the search function worked as exp ...

Interacting with external domains through ajax queries

I'm encountering an issue with my javascript code that sends an ajax request, but I'm not receiving any response. Can anyone offer some guidance on how to troubleshoot this problem? Thank you! The URL I am attempting to retrieve is: Here's ...

Creating a seamless vertical marquee of several images that continuously scroll from bottom to top without interruption can be achieved by following these

Currently, I am seeking to design a mobile-friendly HTML page that features a border with multiple color images moving smoothly from bottom to top on both the right and left sides of the mobile screen. The transition should be seamless without any gaps o ...

method for sorting labels in Select element in ReactJS

Hey there, I'm facing an issue with the code snippet available here. I would really appreciate it if you could assist me in resolving this problem. This is the code: import React from "react"; import { Select } from "antd" ...

Altering the innerHTML of a <p> tag with a smooth transition

I am a beginner in the world of web design. I am currently working on a project where I need to continuously change the text inside a <p> element with a transition effect similar to a slideshow. Below is the code I have so far: <p id="qu">S ...

Acquire information from an Angular service and output it to the console

I am having trouble logging data from my service file in the app.component file. It keeps showing up as undefined. Below is the code snippet: service.ts getBillingCycles() { return this.http.get('../../assets/download_1.json'); }; app.com ...

A step-by-step guide to displaying API data in a React frontend using functional components

I've managed to create a backend API using Node+Express+MSSQL and tested the routes with POSTMAN. However, I'm facing challenges when trying to display the data on the front-end using React Functional components/hooks. I'm unsure if I'm ...

Discovering a Match within a JavaScript Object and Array with the help of Jquery's Each

I am currently working on implementing an array of filters to search through a JavaScript object containing store locations. My goal is to find a full match using both filters from the filters array, which currently has 2 items but will eventually have mor ...

Contrast between utilizing a WebApp that connects to an API and one that relies on backend

Whenever I develop simple web applications, I often begin by setting up a nodeJS backend, usually creating an API server with ExpressJS. When specific routes are accessed, the server responds by dynamically rendering HTML from EJS based on the current conn ...

Create a dropdown menu using a PDO query to populate the selectable options

I need to create a Select List that dynamically populates items/information from a Query. I understand the importance of updating the select list every time the database is updated, so JQuery is required for this task. Although I have limited experience wi ...

Unable to incorporate node-vibrant into Angular 7 project

Currently facing some challenges while attempting to integrate node-vibrant into my Angular 7 project: -Successfully imported with import * as Vibrant from 'node-vibrant';, but encountering a warning in VS Code: Module '"/Users/xxxx/Docume ...

Launching a React project using the terminal - my step-by-step process

I'm attempting to initiate a new react-app utilizing the command npx create-react-app <app name> as shown below: npx create-react-app new-app However, after downloading, it seems to be stuck. It's not progressing at all. I've even rei ...

Basic inquiries concerning Vue.js and JavaScript

Hey there, I recently developed a small app to practice my Vue skills. However, there are a few features that I would like to implement but I'm not sure how to do it just yet. <div class="container" id="app"> <div class="row"> <d ...

Is it possible to transfer files using web-bluetooth technology?

As I work on developing an embedded system that counts the number of cars, saves their speed and time data in a logs file using rsyslog. Simultaneously, I am creating a web-API (in Typescript/Angular with Electron for Desktop usage and later Web as well) t ...

Create a data attribute object and assign to it the prop object received from the parent component

I am struggling with passing an object as a prop from a parent component and then utilizing it to initialize the child component with the received value. The main objective behind this is to create a dialog box that includes a child form component with mu ...

I'm encountering an issue with my function in Vuejs where it is only going through one loop before breaking out. How can I

I'm attempting to validate all items in the cart and disable the sell button if the item is already in the cart (I have this implemented for other functionalities). It seems like my loop is only iterating once before stopping. Any suggestions on how I ...

Implementing a PHP button update functionality sans utilizing an HTML form

I need a way to update my database with just a click of a button, without using any forms or POST requests. Most examples I've seen involve updating through forms and the $_POST method. Is there a simpler way to update the database by directly click ...