Цепочка некоторых асинхронных задач в fp-ts, сохраняющая результат каждой задачи - PullRequest
1 голос
/ 25 октября 2019

В fp-ts я пытаюсь связать некоторые потенциально неудачные асинхронные задачи вместе с TaskEither, но мне нужно использовать результаты промежуточных задач позже по цепочке.

В этом примере:

const getFoo = (a: string): Promise<Foo> => {};
const getBar = (foo: Foo): Promise<Bar> => {};
const mkFooBar = (foo: Foo, bar: Bar): Promise<FooBar> => {};

const async main1: Promise<FooBar> => {
  const a = "a";
  const foo = await getFoo(a);
  const bar = await getBar(foo);
  const fooBar = await mkFooBar(foo, bar);

  return Promise.resolve(fooBar);
};

const main2: Promise<FooBar> => {
  const a = "a";

  return pipe(
    TE.tryCatch(() => getFoo(a), e => e),
    TE.chain(foo => TE.tryCatch(() => getBar(foo), e => e)),
    TE.chain(bar => TE.tryCatch(() => mkFooBar(??, bar), e => e))
  );
};

функция main1 является решением этой проблемы в стиле async/await. Я пытаюсь эмулировать что-то подобное в стиле fp-ts chain. main2 - моя попытка сделать это.

Поскольку версия async/await вводит все промежуточные результаты в локальную область (т. Е. foo и bar), легко вызвать mkFooBar, который зависитна оба этих результата.

Но в версии fp-ts промежуточные результаты попадают в область действия каждой задачи.

Единственный способ заставить эту версию работать -чтобы заставить либо сами асинхронные функции (то есть getFoo и getBar) также возвращать свои аргументы, либо, возможно, оболочки TaskEither возвращают аргументы, чтобы затем их можно было передать следующей функциив цепочке.

Это был бы правильный способ сделать это? Или есть более простая версия, которая больше напоминает версию async/await?

1 Ответ

0 голосов
/ 26 октября 2019

В зависимости от того, сколько раз вам понадобится получить доступ к промежуточным результатам в следующих вычислениях, я бы предложил либо использовать Do из fp-ts-contrib (приближение к записи Хаскелла do), либопередать промежуточные результаты вручную map ping.

Дано:

import * as TE from "fp-ts/lib/TaskEither";

declare function getFoo(a: string): TE.TaskEither<unknown, Foo>;
declare function getBar(foo: Foo): TE.TaskEither<unknown, Bar>;
declare function mkFooBar(foo: Foo, bar: Bar): TE.TaskEither<unknown, FooBar>;

Пример с Do:

import { Do } from "fp-ts-contrib/lib/Do";

function main2(): TE.TaskEither<unknown, FooBar> {
  return Do(TE.taskEither)
    .bind("foo", getFoo("a"))
    .bindL("bar", ({ foo }) => getBar(foo))
    .bindL("fooBar", ({ foo, bar }) => mkFooBar(foo, bar))
    .return(({ fooBar }) => fooBar);
}

Пример отображения вручную:

import { pipe } from "fp-ts/lib/pipeable";

function main3(): TE.TaskEither<unknown, FooBar> {
  return pipe(
    getFoo("a"),
    TE.chain(foo =>
      pipe(
        getBar(foo),
        TE.map(bar => ({ foo, bar }))
      )
    ),
    TE.chain(({ foo, bar }) => mkFooBar(foo, bar))
  );
}
...