将 TaskEither<E, A>[] 转换为 TaskEither<E[],A[]>
Convert TaskEither<E, A>[] to TaskEither<E[],A[]>
我仍在学习和使用 fp-ts
,但无法弄清楚。
我有一些 API 个调用,我想将所有成功的响应和所有错误收集到数组中。
所以,我尝试使用 array.sequence
:
TE.map(schedules =>
array.sequence(TE.taskEither)(
schedules.map(({ Program, ...schedule }) =>
pipe(
createProgramIfNotExist(Program),
TE.map(createdProgram =>
setRecordingSchedules(programsClient, { ...schedule, ProgramId: createdProgram.Id }),
),
TE.flatten,
),
),
),
),
TE.flatten
这对响应工作正常,但我只收到来自 API 调用的最后一个错误。有什么办法可以把所有的错误收集到一个数组中吗?
下面我写了进行 API 调用的函数,以防万一我在那里遇到问题。
export const setRecordingSchedules = (
fetcher: AxiosInstance,
config: RecordingConfig,
): TE.TaskEither<Error, [ReturnType<typeof Schedule.codec.encode>, number]> => {
const url = `/programs/${config.ProgramId}/recordingschedules`;
return pipe(Schedule.codec.encode({ ...config, AutoPodcastConfig }), body =>
pipe(
TE.tryCatch(
() => handleRateLimit(() => fetcher.put(url, body)),
err => raiseUpdateError(unknownToError(err)),
),
TE.map(({ status }) => [body, status]),
),
);
};
export const createRecordingSchedule = (
fetcher: AxiosInstance,
program: Program.Type,
): TE.TaskEither<Error, Program.Type> =>
pipe(
Program.codec.encode(program),
body =>
pipe(
TE.tryCatch(
() => handleRateLimit(() => fetcher.post('/programs', body)),
err => raiseCreateError(unknownToError(err)),
),
TE.map(({ data }) =>
pipe(
Program.codec.decode({ ...data, Network: program.Network }),
E.bimap(
errors => ReportValidationError(errors, { ...data, Network: program.Network }),
decoded => decoded,
),
TE.fromEither,
),
),
),
TE.flatten,
);
思路是转换TaskEither<E, A>[] -> Task<Either<E, A>[]>
,然后给一个合并函数Either<E, A>[] -> Either<E[], A[]>
就可以实现你想要的了。合并功能也是分几步完成的:
- 为
Either<E[], A[]>
制作一个验证幺半群
Either<E, A> -> Either<E[], A[]>
- 将值或错误简单地包装到单例数组中
foldMap
Either<E, A>[]
通过映射上述包装器并使用上述 monoid 进行折叠
import * as E from 'fp-ts/lib/Either';
import * as A from 'fp-ts/lib/Array';
// type Error = ...
// type Result = ...
// Make a validation monoid for `Either<E[], A[]>`
const validationMonoid = E.getValidationMonoid(A.getMonoid<Error>(), A.getMonoid<Result>());
// `Either<E, A> -> Either<E[], A[]>` - trivial wrap of value or error into singleton arrays
const validationWrap = E.bimap(x => [x], x => [x]);
// `foldMap` `Either<E, A>[]` by mapping with the above wrapper and folding using above monoid
const validationMerge = E.foldMap(validationMonoid)(validationWrap);
现在你可以做
const tasks: TaskEither<Error, Result>[] = ...;
const aggregatedResults: TaskEither<Error[], Result[]> = pipe(
// convert `TaskEither<E, A>[] -> Task<Either<E, A>[]>`
array.sequence(T.task)(tasks),
// and then apply the merge
T.map(validationMerge)
);
解决方案是使用 traverse
。想法是获取验证任务幺半群并将每个 E
合并到一个数组中。
array.traverse(TE.getTaskValidation(A.getMonoid<E>()))(xs, TE.mapLeft(A.of))
完整的工作解决方案:
import { array, getMonoid, of as arrayOf } from 'fp-ts/lib/Array';
TE.chain(schedules =>
array.sequence(TE.taskEither)(
schedules.map(({ Program, ...schedule }) =>
pipe(
createProgramIfNotExist(Program),
TE.map(createdProgram =>
setRecordingSchedules(programsClient, { ...schedule, ProgramId: createdProgram.Id }),
),
),
),
xs => array.traverse(TE.getTaskValidation(getMonoid<E>()))(xs, TE.mapLeft(arrayOf)),
),
),
我仍在学习和使用 fp-ts
,但无法弄清楚。
我有一些 API 个调用,我想将所有成功的响应和所有错误收集到数组中。
所以,我尝试使用 array.sequence
:
TE.map(schedules =>
array.sequence(TE.taskEither)(
schedules.map(({ Program, ...schedule }) =>
pipe(
createProgramIfNotExist(Program),
TE.map(createdProgram =>
setRecordingSchedules(programsClient, { ...schedule, ProgramId: createdProgram.Id }),
),
TE.flatten,
),
),
),
),
TE.flatten
这对响应工作正常,但我只收到来自 API 调用的最后一个错误。有什么办法可以把所有的错误收集到一个数组中吗?
下面我写了进行 API 调用的函数,以防万一我在那里遇到问题。
export const setRecordingSchedules = (
fetcher: AxiosInstance,
config: RecordingConfig,
): TE.TaskEither<Error, [ReturnType<typeof Schedule.codec.encode>, number]> => {
const url = `/programs/${config.ProgramId}/recordingschedules`;
return pipe(Schedule.codec.encode({ ...config, AutoPodcastConfig }), body =>
pipe(
TE.tryCatch(
() => handleRateLimit(() => fetcher.put(url, body)),
err => raiseUpdateError(unknownToError(err)),
),
TE.map(({ status }) => [body, status]),
),
);
};
export const createRecordingSchedule = (
fetcher: AxiosInstance,
program: Program.Type,
): TE.TaskEither<Error, Program.Type> =>
pipe(
Program.codec.encode(program),
body =>
pipe(
TE.tryCatch(
() => handleRateLimit(() => fetcher.post('/programs', body)),
err => raiseCreateError(unknownToError(err)),
),
TE.map(({ data }) =>
pipe(
Program.codec.decode({ ...data, Network: program.Network }),
E.bimap(
errors => ReportValidationError(errors, { ...data, Network: program.Network }),
decoded => decoded,
),
TE.fromEither,
),
),
),
TE.flatten,
);
思路是转换TaskEither<E, A>[] -> Task<Either<E, A>[]>
,然后给一个合并函数Either<E, A>[] -> Either<E[], A[]>
就可以实现你想要的了。合并功能也是分几步完成的:
- 为
Either<E[], A[]>
制作一个验证幺半群
Either<E, A> -> Either<E[], A[]>
- 将值或错误简单地包装到单例数组中foldMap
Either<E, A>[]
通过映射上述包装器并使用上述 monoid 进行折叠
import * as E from 'fp-ts/lib/Either';
import * as A from 'fp-ts/lib/Array';
// type Error = ...
// type Result = ...
// Make a validation monoid for `Either<E[], A[]>`
const validationMonoid = E.getValidationMonoid(A.getMonoid<Error>(), A.getMonoid<Result>());
// `Either<E, A> -> Either<E[], A[]>` - trivial wrap of value or error into singleton arrays
const validationWrap = E.bimap(x => [x], x => [x]);
// `foldMap` `Either<E, A>[]` by mapping with the above wrapper and folding using above monoid
const validationMerge = E.foldMap(validationMonoid)(validationWrap);
现在你可以做
const tasks: TaskEither<Error, Result>[] = ...;
const aggregatedResults: TaskEither<Error[], Result[]> = pipe(
// convert `TaskEither<E, A>[] -> Task<Either<E, A>[]>`
array.sequence(T.task)(tasks),
// and then apply the merge
T.map(validationMerge)
);
解决方案是使用 traverse
。想法是获取验证任务幺半群并将每个 E
合并到一个数组中。
array.traverse(TE.getTaskValidation(A.getMonoid<E>()))(xs, TE.mapLeft(A.of))
完整的工作解决方案:
import { array, getMonoid, of as arrayOf } from 'fp-ts/lib/Array';
TE.chain(schedules =>
array.sequence(TE.taskEither)(
schedules.map(({ Program, ...schedule }) =>
pipe(
createProgramIfNotExist(Program),
TE.map(createdProgram =>
setRecordingSchedules(programsClient, { ...schedule, ProgramId: createdProgram.Id }),
),
),
),
xs => array.traverse(TE.getTaskValidation(getMonoid<E>()))(xs, TE.mapLeft(arrayOf)),
),
),