0

假设我有三个实体,一个父实体,代表通过第三方 API 摄取数组数组,一个子实体代表一个此类数组成员的解析和处理,错误代表处理失败以及错误消息。

假设我有以下类型:

export type CreateChildEntity = () => TaskEither<ServiceError, void>;

export type CheckParentMappable = (
  id: UUID
) => TaskEither<ServiceError, Option<UUID>>;

export type GetHydratedParentForMapping = (
  id: UUID
) => TaskEither<ServiceError, HydratedType>;

export type MarkParentMapped = (id: UUID) => TaskEither<ServiceError, void>;

export type MarkParentFailed = (id: UUID) => TaskEither<ServiceError, void>;

export type LookupDetails = (
  id: UUID,
  firstName: string,
  lastName: string
) => TaskEither<ServiceError, Option<ValidatedDetails>>;

export type TrackParentMappingError = (
  id: UUID,
  author: ValidatedAuthor,
  message: string
) => TaskEither<ServiceError, void>;

export type Deps = Readonly<{
  createChildEntity: CreateChildEntity;
  checkParentMappable: CheckParentMappable;
  getHydratedParentForMapping: GetHydratedParentForMapping;
  markParentMapped: MarkParentMapped;
  markParentFailed: MarkParentFailed;
  lookupDetails: LookupDetails;
  trackParentMappingError: TrackParentMappingError;
}>;

然后是以下功能:

import { Ctx, Deps, Input, Error } from "./types";
import { pipe } from "fp-ts/lib/pipeable";
import * as TE from "fp-ts/lib/TaskEither";
import { isSome } from "fp-ts/lib/Option";

export default (input: Input): Ctx<void> => mapParent(input);

const mapParent = (input: Input): Ctx<void> => (
  deps: Deps
): TE.TaskEither<Error, void> =>
  pipe(
    deps.getHydratedParentForMapping(input.id),
    TE.chain(hydratedParent =>
      pipe(
        TE.sequenceArray(
          hydratedParent.authors.map(a => {
            return pipe(
              deps.lookupDetails(hydratedParent.id, a.firstName, a.lastName),
              TE.chain(detail => {
                if (isSome(detail)) {
                  return deps.createChildEntity();
                } else {
                  return pipe(
                    deps.trackParentMappingError(
                      hydratedParent.id,
                      a,
                      "MISSING_DETAILS_ERROR"
                    ),
                    TE.chain(_ => deps.markParentFailed(hydratedParent.id))
                  );
                }
              })
            );
          })
        ),
        TE.chain(_ => deps.markParentMapped(hydratedParent.id))
      )
    )
  );

完整代码在这里:https ://stackblitz.com/edit/typescript-3qv4t1?file=index-bad.ts

只要我在里面有验证规则markParentFailedmarkParentMapped确定父级确实(或没有)附加了子级/错误,这段代码就可以工作。

相反,我想要实现的是让 cross 的初始映射lookupDetails返回hydratedParent.authors一个 Either,这样我就可以根据故障的存在完全划分流。实际上,像这样:

const constructEither = (hydratedParent: HydratedType): Ctx<void> => (deps: Deps): TE.TaskEither<Error, Either<ReadonlyArray<Readonly<{id: UUID, author: ValidatedAuthor, errorMessage: string}>>, ReadonlyArray<ValidatedDetails>>> => {
  return TE.sequenceArray(
    hydratedParent.authors.map(a => {
      return pipe(
        deps.lookupDetails(hydratedParent.id, a.firstName, a.lastName),
        TE.chain(val => {
          if(isSome(val)) {
            return right(val.value)
          } else {
            return left({id: hydratedParent.id, author: a, errorMessage: "MISSING_DETAILS_ERROR"})
          }
        })
      )
    }))
}

const mapParent = (input: Input): Ctx<void> => (
  deps: Deps
): TE.TaskEither<Error, void> =>
  pipe(
    deps.getHydratedParentForMapping(input.id),
    TE.chain(hydratedParent =>
    pipe(
      constructEither(deps)(hydratedParent),
      TE.chain(res => {
        if(isRight(res)) {
          pipe(
            res.map(r => deps.createChildEntity()),
            ,TE.chain(_ => deps.markParentMapped(hydratedParent.id))
          )
        } else {
          pipe(
            res.map(r => deps.trackParentMappingError(
              r.left.id,
              r.left.author,
              r.left.errorMessage
            )),
            TE.chain(_ => deps.markParentFailed(hydratedParent.id))
          )
        }
      })
    )
    )
  );

这无法编译。

此外,我的理解是,即使我让它编译,TaskEither 无论如何都会忽略左分支,并且实际上将它作为异常抛出。

以这种方式对 TaskEither 进行分区的 fp-ts 咒语是什么?

4

0 回答 0