Skip to content

Instantly share code, notes, and snippets.

@tusharmath
Last active March 31, 2023 01:05
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tusharmath/f1eaa6e0bdb3cc2c37835497a85c0b60 to your computer and use it in GitHub Desktop.
Save tusharmath/f1eaa6e0bdb3cc2c37835497a85c0b60 to your computer and use it in GitHub Desktop.
Trampolining in Typescript
// stub for typescript
declare function BigInt(n: number): number
abstract class Trampoline<A> {
abstract map<B>(ab: (a: A) => B): Trampoline<B>
abstract flatMap<B>(ab: (a: A) => Trampoline<B>): Trampoline<B>
zip<B>(tb: Trampoline<B>): Trampoline<[A, B]> {
const ta = this
return ta.flatMap(a => tb.map(b => [a, b] as [A, B]))
}
}
class Done<A> extends Trampoline<A> {
constructor(readonly a: A) {
super()
}
map<B>(ab: (a: A) => B): Trampoline<B> {
return new Done(ab(this.a))
}
flatMap<B>(ab: (a: A) => Trampoline<B>): Trampoline<B> {
return ab(this.a)
}
}
class More<A> extends Trampoline<A> {
constructor(readonly fn: () => Trampoline<A>) {
super()
}
map<B>(ab: (a: A) => B): Trampoline<B> {
return new More(() => this.fn().map(ab))
}
flatMap<B>(ab: (a: A) => Trampoline<B>): Trampoline<B> {
return new More(() => this.fn().flatMap(ab))
}
}
function done<A>(a: A): Done<A> {
return new Done(a)
}
function more<A>(a: () => Trampoline<A>): More<A> {
return new More(a)
}
const interpret = <ARGS extends any[], R>(fn: (...t: ARGS) => Trampoline<R>) => {
return (...t: ARGS): R => {
let result: Trampoline<any> = fn(...t)
while (true) {
if (result instanceof More) {
result = result.fn()
} else if (result instanceof Done) {
return result.a
}
}
}
}
// Simple implementation that throws a stack overflow exception
const fib = (desiredCount: number): number => {
const itar = (a: number, b: number, count: number): number => {
if (count === desiredCount) {
return a + b
} else {
return itar(b, a + b, count + 1)
}
}
return itar(BigInt(0), BigInt(1), 0) // recursive call
}
// Works without any issues
const fib_TRAMPOLINED = (desiredCount: number): Trampoline<number> => {
const itar = (a: number, b: number, count: number): Trampoline<number> => {
if (count === desiredCount) {
return done(a + b)
} else {
return more(() => itar(b, a + b, count + 1))
}
}
return itar(BigInt(0), BigInt(1), 0) // recursive call
}
// RangeError: Maximum call stack size exceeded
// console.log(fib(10000))
// Works yay!
console.log(interpret(fib_TRAMPOLINED)(10000))
@rahuldream11
Copy link

rahuldream11 commented Nov 26, 2018

How it will work if program is

const fib = (desiredCount: number): number => {
  const itar = (a: number, b: number, count: number): number => {
    if (count === desiredCount) {
      return a + b
    } else {
      return a+itar(b, a + b, count + 1)
    }
  }
  return itar(BigInt(0), BigInt(1), 0) // recursive call
}

@tusharmath
Copy link
Author

tusharmath commented Dec 5, 2018

This is a more complex use case where we would need to implement more advanced features inside of trampoline. —

Trampoline

abstract class Trampoline<A> {
  abstract map<B>(ab: (a: A) => B): Trampoline<B>
  abstract flatMap<B>(ab: (a: A) => Trampoline<B>): Trampoline<B>


  // The new ZIP operator can help us with combining two Trampolines.
  zip<B>(tb: Trampoline<B>): Trampoline<[A, B]> {
    const ta = this
    return ta.flatMap(a => tb.map(b => [a, b] as [A, B]))
  }
}

class Done<A> extends Trampoline<A> {
  constructor(readonly a: A) {
    super()
  }
  map<B>(ab: (a: A) => B): Trampoline<B> {
    return new Done(ab(this.a))
  }
  flatMap<B>(ab: (a: A) => Trampoline<B>): Trampoline<B> {
    return ab(this.a)
  }
}

class More<A> extends Trampoline<A> {
  constructor(readonly fn: () => Trampoline<A>) {
    super()
  }

  map<B>(ab: (a: A) => B): Trampoline<B> {
    return new More(() => this.fn().map(ab))
  }
  flatMap<B>(ab: (a: A) => Trampoline<B>): Trampoline<B> {
    return new More(() => this.fn().flatMap(ab))
  }
}

@loveqoo
Copy link

loveqoo commented Feb 10, 2021

Very useful code for me. thanks a lot!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment