A library should support your decisions while allowing you to leave an impression for others to follow.
I made this little library of helper functions to aid my development of javascript/typescript applications and learn more about the functional paradigm.
Let's look at how we might use this lib. Imagine we are creating an art gallery, and we want to take a closer look at one of the pieces. To get the piece of art we send a request to a backend referencing it by id. The response might take a little while, so we need a way to notify the user that the request is pending. Once we have obtained the piece we will need to display it, or if the call has failed we need to notify that something went wrong.
In the example below we request the art via ID. While we wait, we notify the user that the content is loading. The onPending function will fire the provided callback twice. Once when it is invoked and again once the call is done, passing the pending state (true then false) as a parameter. Once the call is complete it will invoke either onSuccess with the data or onFailure with a possible explanation.
getArt(id)
.onPending(isLoading)
.onSuccess(updatePiece)
.onFailure(hasErrored);
To handle the request, we make a http GET to the endpoint with the id. We validate the response, if the response is structured correctly pass back the successful response, else pass back a failure with some explanation.
getArt: (id: string): Result.Async<Art, AnError> =>
http.get(`/some-endpoint/${id}`)
.mBind(response => maybe(valid(response))
.map(asyncSuccess)
.orElse(asyncFailure({type: Problem.CANNOT_DECODE, cause: response})))
To make the request, we fetch from the endpoint. If there is some kind of network error we give back an explanation. If successful, we check the response status. Since it's a GET we expect a 200 is a successful response, or we consider it a failure. Then we get the JSON out of the response. If there is a problem with the JSON we pack it into an explanation.
get: (endpoint: string): Result.Async<Art, AnError> =>
asyncResult(fetch(endpoint))
.or(err => asyncFailure({type: Problem.NETWORK_ERROR, cause: err}))
.mBind(response => response.status === HTTPStatus.OK
? asyncResult(response.json())
: asyncFailure({type: Problem.NOT_OK, cause: response}));