如何避免 fp-ts 中的嵌套 Monad 或优雅地处理它们?
How do I avoid nested Monads in fp-ts or deal with them elegantly?
我有一系列代码需要经过以下步骤(伪代码):
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
如果我 map/chain 这些步骤在一起,我最终会得到像 TaskEither<IError, Either<IError, TaskEither<IError, IOpenJob>>>
这样的类型,这显然有点尴尬。
我目前将所有这些扁平化为一个简单的 TaskEither<IError, IJobOpenedEvent>
类型的解决方案如下所示(真实代码):
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);
我的问题是 - 有没有更好的方法来处理这种嵌套?我觉得我做错了,因为我是函数式编程的新手并且不了解所有理论。拥有所有嵌套的 flatten(taskEither)
调用并将 Either
转换为带有 fromEither
的 TaskEither
对我来说似乎很糟糕。
非常感谢任何帮助!
感谢 Bergi 的评论,我使用 chain
而不是 map
找到了以下解决方案:
export const openJobHandler = (
command: IOpenJobCommand
): TaskEither<IError, IJobOpenedEvent> =>
jobsRepository
.findById(command.jobId)
.map(jobs.openJob)
.chain(fromEither)
.chain(jobsRepository.update)
.map(createJobOpenedEvent)
我有一系列代码需要经过以下步骤(伪代码):
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
如果我 map/chain 这些步骤在一起,我最终会得到像 TaskEither<IError, Either<IError, TaskEither<IError, IOpenJob>>>
这样的类型,这显然有点尴尬。
我目前将所有这些扁平化为一个简单的 TaskEither<IError, IJobOpenedEvent>
类型的解决方案如下所示(真实代码):
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);
我的问题是 - 有没有更好的方法来处理这种嵌套?我觉得我做错了,因为我是函数式编程的新手并且不了解所有理论。拥有所有嵌套的 flatten(taskEither)
调用并将 Either
转换为带有 fromEither
的 TaskEither
对我来说似乎很糟糕。
非常感谢任何帮助!
感谢 Bergi 的评论,我使用 chain
而不是 map
找到了以下解决方案:
export const openJobHandler = (
command: IOpenJobCommand
): TaskEither<IError, IJobOpenedEvent> =>
jobsRepository
.findById(command.jobId)
.map(jobs.openJob)
.chain(fromEither)
.chain(jobsRepository.update)
.map(createJobOpenedEvent)