2

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))
          )
        );
      });
    }
  )
);
Arun Gopalpuri
  • 2,340
  • 26
  • 27

1 Answers1

4

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.

ford04
  • 66,267
  • 20
  • 199
  • 171
  • `chain` takes the computed value from previous sequence in pipe. Is that right? – Arun Gopalpuri Jun 13 '20 at 18:28
  • 2
    yes, in above case `TE.chain(fetchUser)` resolves to a function that takes the `TaskEither` from previous pipe segment and returns a new transformed one. – ford04 Jun 13 '20 at 19:08