An Effect
is a full description of a program. We can compose Effect
with each other to build other Effect
that describe more complex programs.
In this example we have 2 effects fetchRequest
and jsonResponse
:
fetchRequest
returns an effect that containsResponse
- We want to extract
Response
from the first effect and provide it tojsonResponse
The problem is that we wrapped Response
inside Effect
(Effect<Response>
). We cannot just "extract" Response
anymore:
const fetchRequest = Effect.promise(() =>
fetch("https://pokeapi.co/api/v2/pokemon/garchomp/")
);
const response: Response = /// No way to directly access `Response` 😬
Effects are computations. They don't hold the
Response
.It is impossible to get the
Response
out of an effect because theResponse
does not exist (yet).Evaluating the effect will result in a
Response
.
Instead effect provides functions that describe what's the next step to execute based on the value from the previous effect(s).
flatMap: Get value and return Effect
Effect.flatMap
allows to access the result of an effect and chain another Effect
:
- First parameter:
Effect
from where we want to extract the value - Second parameter: function that gives access to the first
Effect
parameter and returns anotherEffect
const fetchRequest = Effect.promise(() =>
fetch("https://pokeapi.co/api/v2/pokemon/garchomp/")
);
const main = Effect.flatMap(
fetchRequest, // 👈 Extract value from `fetchRequest`
(response) => // 👈 Access `Response` and return another `Effect`
);
With Effect.flatMap
we can chain jsonResponse
since we get access to response
:
/// 👇 `Effect<Response>`
const fetchRequest = Effect.promise(() =>
fetch("https://pokeapi.co/api/v2/pokemon/garchomp/")
);
/// 👇 `Effect<unknown>` (return type of `jsonResponse`)
const main = Effect.flatMap(
fetchRequest,
(response) => jsonResponse(response)
);
This may look weird. No more linear imperative code one line after the other, but instead using functions that take as input other functions and return functions 🙌
Bare with me for now, effect offers some way to write way better-looking code that we are going to introduce later.
We can then run the final program using runPromise
:
runPromise
is the equivalent ofrunSync
but for asynchronous programs
const fetchRequest = Effect.promise(() =>
fetch("https://pokeapi.co/api/v2/pokemon/garchomp/")
);
const jsonResponse = (response: Response) =>
Effect.promise(() => response.json());
const main = Effect.flatMap(
fetchRequest,
(response) => jsonResponse(response)
);
Effect.runPromise(main);
We can even simplify the code to:
const fetchRequest = Effect.promise(() =>
fetch("https://pokeapi.co/api/v2/pokemon/garchomp/")
);
const jsonResponse = (response: Response) =>
Effect.promise(() => response.json());
const main = Effect.flatMap(
fetchRequest,
jsonResponse
);
Effect.runPromise(main);