Add Fuel To Your Functions

Hi there, I'm a Software developer and have been programming since 2010.
I came in contact with functional programming when I did my master's in computer mathematics. I learned functional programming using Lisp, Prolog and Mathematica. But then I found my perfect language in Elm and never looked back.
At my job, I work with Kotlin and Typescript, which work best by writing a hybrid of FP and OOP.
Ever since I started working, I use field notes to track my thoughts and discoveries. My blog contains the best parts of those notes.
If you run a heavy computation in the frontend, the main thread gets blocked and the browser tab freezes.
/**
* Sieve Of Eratosthenes as an example for some very heave computation
*/
function sieveOfEratosthenes(state:{next:int, primes:Set<int>}) {
let found = false;
for (const n of state.primes) {
if (state.next % n === 0) {
found = true;
break;
}
}
if (!found) {
state.primes.add(state.next);
}
state.next++;
return state;
}
function someHeavyComputation(){
const state = {next: 2, primes: new Set()}
while (state.primes.size < 100) {
sieveOfEratosthenes(state)
}
return state;
}
someHeavyComputation()
There is a simple design pattern that helps avoid this issue: Fuel.
function someHeavyComputation(fuel, state) {
while (state.primes.size < 100 && fuel > 0) {
sieveOfEratosthenes(state);
fuel -= 1;
}
return state;
}
let state = { next: 2, primes: new Set() };
state = someHeavyComputation(5, state);
//do something else
state = someHeavyComputation(5, state);
//do something else
state = someHeavyComputation(5, state);
//and so on.
Fueled computation allows you to pause heavy tasks and save partial results.
Things you can do with fuel:
Implement a progress bar
Display partial results and update them over time.
Recover after a page refresh (or accidentally closing the tab)
Compute the results in the background while the user interacts with the UI
I am using JavaScript here as an example, but the same concept, of course, works for other languages as well.
Using Generators
JavaScript (and other languages like Python and Kotlin) actually has generator to make the fueled execution into a interator.
function* sieveOfEratosthenes() {
let next = 2;
const primes = new Set();
while (true) {
let found = false;
for (const n of primes) {
if (next % n === 0) {
found = true;
break;
}
}
if (!found) {
primes.add(next);
}
next++;
yield primes;
}
}
const iterator = sieveOfEratosthenes();
function someHeavyComputation(fuel,primes) {
while (result.size < 100 && fuel > 0) {
primes = iterator.next().value;
fuel -= 1;
}
return primes;
}
let primes = new Set();
primes = someHeavyComputation(5, primes);
//do something else
primes = someHeavyComputation(5, primes);
//do something else
primes = someHeavyComputation(5, primes);
//and so on.
An alternative would be to use a Web Worker instead to keep the main thread untouched. While this helps keep the tab from freezing, it can still lead to long wait times without any direct feedback.



