0

I'm very new to Functional Programming and specially fp-ts.

What I'm struggling recently is playing with arrays and chaining them into different Monads and at the same time preserving some details related to each step.

Let's say we have a program that

  1. fetchs some data as string
  2. parses the string into array of ITitle if possible; type can be Option<ITitle[]>
  3. maps them into an array of possible ITitle performing some async updates; type can be TaskEither<IError, ITitle>
  4. Prints the result (if available) including the failures with their specific errors
// error definition
enum ErrorType {
    NotFound = 'NOT_FOUND',
    Timeout = 'TIMEOUT',
    ParseFailure = 'PARSE_FAILURE',
    UpdateFailure = 'UPDATE_FAILURE',
    Unknown = 'UNKNOWN',
}
// our types
interface ITitle {
    title: string;
    meta: unknown;
}
interface IError {
    msg: string;
    type: ErrorType;
}

// helper functions
declare function findTitles(): TaskEither<IError, string>;
declare function parseTitles(items: string): Option<ITitle[]>; // or Either<IError, ITitle[]> whatever fits best
declare function updateTitle(title: ITitle): TaskEither<IError, ITitle>;

Update:
I'm updating the question with more specific details where the problem is:

const program = pipe(
  findTitles(),
  TE.map(parseTitles), // Now we get TaskEither<IError, Option<ITitle[]>>;
  TE.chain(titles =>
    // Problem is var titles is still wrapped in Option and this won't work
    // What if the titles is Either<IError, ITitle[]>?
    // How do we preserve the error if titles is an Either?
    array.traverse(taskEither)(titles, title => updateTitle(title))
  ),
  // How to log the result including the errors?
  // Or in another word how do we run traverse and fold the Eithers?
)
Amin Paks
  • 276
  • 2
  • 15

1 Answers1

2

TE.map(parseTitles) gets us a TaskEither<IError, Option<ITitle[]>>, problem is in chain callback where titles is still wrapped in Option and this won't work

You can add another chain that unwraps the Option into a TaskEither, using fromOption:

const program = pipe(
  findTitles(),
  TE.map(parseTitles), // Now we have TaskEither<IError, Option<ITitle[]>>;
  TE.chain(TE.fromOption(() => ErrorType.NotFound)) // Now we have TaskEither<IError, ITitle[]>
  TE.chain(titles =>
    array.traverse(taskEither)(titles, title => updateTitle(title))
  ),
)

or fromEither:

const program = pipe(
  findTitles(),
  TE.map(parseTitles), // Now we have TaskEither<IError, Either<IError, ITitle[]>>;
  TE.chain(TE.fromEither) // Now we have TaskEither<IError, ITitle[]>
  TE.chain(titles =>
    array.traverse(taskEither)(titles, title => updateTitle(title))
  ),
)
Bergi
  • 630,263
  • 148
  • 957
  • 1,375