The IO Functor in TypeScript

Dimitris Papadimitriou
3 min readJan 25, 2020

--

Thunks

A thunk ()=>{…} is a subroutine used to inject an additional calculation into another subroutine. Thunks are primarily used to delay a calculation until its result is needed, or to insert operations at the beginning or end of the other subroutine. For example, a very common pattern is to initialize an object only when it is needed. This is called Lazy initialization because if we have a very costly computation that may not be needed, we should not eagerly evaluate it.

var bigArrayComputation = [...Array(10e6)].map((_, i) => i).reduce((a, i) => a + i)MayOrnMayNotDisplay(bigArrayComputation)

Instead of eager initialization, we can the creation or evaluation with a factory method that is only performed once if/and when it is needed.

Run This: TS Fiddle

We can extend this lazy type in order to provide a Map method

map<T1>(f: (y: T) => T1): Lazy<T1> {    return new Lazy<T1>(() => f(this.Fn()))};

Run This: TS Fiddle

The mechanics behind the mapping function that lifts the function f: T →T1 follows the same steps as Promise<T>:

1. Waits for the result of the Lazy (so in a way unwraps the contained value) this.Fn()

2. Applying the function f : f(this.Fn()

3. wraps the resulting value again into a new Lazy: Lazy<T1>(() => f(this.Fn()))

because when we implement a map, we always return something of the same type in order to belong to the same category (in this case type) or Promise.

You must appreciate that this preserves the Laziness of the Lazy<T> at no point the Lazy must collapse without trying to access the outer Value. At the step 3 we apply a lazy wrapper () => around what seems as evaluation this.Value which thus is not executed.

IO Functor

Functional programming in the same lines provides the IO functor

Run This: TS Fiddle

the map implementation should remind you slightly of the promise Map line of reasoning while implementing it. We first have to “run” the previous computation Fn() then use the function f to get the lifted value f(Fn()) and then finally rewrap this new value in a new new IO<T1>(() => f(Fn())).

We also define a Run method usually that just executes explicitly the computation

public T Run() => matchWith (x => x);

An example application coming from the frontend web design universe uses IO, to isolate the effects of accessing the DOM elements with jQuery. This is considered Side effect because there is no predictability on the Input. It’s not Referential Transparent.

It’s the same as the Id functor but encloses the value that we provide in a thunk () => { } just to make it lazy. We would have to collapse the function Fn() in order to get back the value, most IO implementations around usually provide a run () function that does just that.

Excerpt from the book :

https://leanpub.com/functional-programming-in-Ts-with-categories

--

--