简体   繁体   中英

How do I avoid nested Monads in fp-ts or deal with them elegantly?

I have a sequence of code which needs to go through the below steps (pseudo code):

  jobsRepository.findById // <-- this returns a TaskEither
  jobs.openJob // <-- jobs.openJob returns an Either
  jobsRepository.update // <-- this returns another TaskEither
  createJobOpenedEvent // simple function that returns an IJobOpenedEvent
                       // given an IOpenJob

If I map/chain those steps together I end up with a type like TaskEither<IError, Either<IError, TaskEither<IError, IOpenJob>>> which is obviously a bit awkward.

My current solution to flatten all of this into a simple TaskEither<IError, IJobOpenedEvent> type looks like the following (real code):

import { flatten } from "fp-ts/lib/Chain";
import { Either, either, left, right } from "fp-ts/lib/Either";
import {
  fromEither,
  TaskEither,
  taskEither,
  tryCatch,
} from "fp-ts/lib/TaskEither";

const createJobOpenedEvent = (openJob: jobs.IOpenJob): IJobOpenedEvent => ({
  name: "jobOpened",
  payload: openJob,
});

export const openJobHandler = (
  command: IOpenJobCommand
): TaskEither<IError, IJobOpenedEvent> =>
  flatten(taskEither)(
    flatten(taskEither)(
      jobsRepository
        .findById(command.jobId) // <-- this returns a TaskEither<IError, IJob>
        .map(jobs.openJob) // <-- jobs.openJob returns an Either<IError, IOpenJob>
        .map(fromEither)
        .map(jobsRepository.update) // <-- this returns a TaskEither<IError,IOpenJob>
    )
  ).map(createJobOpenedEvent);


My question is - is there a better way to handle this nesting? I feel like I am doing this wrong as I am new to functional programming and don't understand all the theory. Having all the nested flatten(taskEither) calls and converting an Either into a TaskEither with fromEither seems bad to me.

Any help is much appreciated!

Thanks to Bergi for his comment I was able to find the following solution using chain instead of map :

export const openJobHandler = (
  command: IOpenJobCommand
): TaskEither<IError, IJobOpenedEvent> =>
  jobsRepository
    .findById(command.jobId)
    .map(jobs.openJob)
    .chain(fromEither)
    .chain(jobsRepository.update)
    .map(createJobOpenedEvent)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM