Arun Gopalpuri
Arun Gopalpuri

Reputation: 2473

fp-ts/typescript avoid nested pipes

How can I avoid nested pipes when using fp-ts in typescript? Do notation? Here's an example of nested pipe I want to avoid

  pipe(
  userId,
  O.fold(
    () => setUser('No user found'),
    (uId: string) => {
      fetchUser(uId).then((response: Either<NonEmptyArray<string>, User>) => {
        pipe(
          response,
          E.fold(
            () => setUser('Unable to fetch user'),
            (user: User) => setUser(JSON.stringify(user, null, 2))
          )
        );
      });
    }
  )
);

Upvotes: 2

Views: 1368

Answers (1)

ford04
ford04

Reputation: 74510

One way is to delay fold as long as possible to avoid unnecessary nesting.

Algebraic effects can be composed without needing to know, if a value is existent or an operation has failed.

For example, a TaskEither transformed with chain will keep the first error, if no user has been found. Otherwise, it contains either the fetchUser error or the User data in success case.

Working example

import { pipeable as P, option as O, taskEither as TE, nonEmptyArray as NA } from "fp-ts";

type User = { name: string };

// global state (side effect)
let user: string | undefined = undefined;
const setUser = (usr: string) => (user = usr);

// given input
const userId: O.Option<string> = O.some("Arun");

const fetchUser: (uid: string) => TE.TaskEither<NA.NonEmptyArray<string>, User> = uid =>
  TE.taskEither.of({ name: "Arun" });
  // An error case would be: TE.left(NA.of("Unable to fetch user"))  

const res = P.pipe(
  userId,
  TE.fromOption(() => NA.of("No user found")),
  TE.chain(fetchUser),
  TE.map(user => JSON.stringify(user, null, 2)),
  TE.fold( // we fold at the very end before applying the effect
    err => TE.taskEither.fromIO(() => { setUser(err[0]); }),
    user => TE.taskEither.fromIO(() => { setUser(JSON.stringify(user, null, 2)); })
  ),
  TE.chain(() => TE.taskEither.fromIO(() => { console.log(user); }))
);

// run the effect
res();

PS: I assumed here, your fetchUser is an async operation, which creates TaskEither. You can switch it back to Either, if needed.

Upvotes: 4

Related Questions