1.) Как превратить монады из одного типа в другой, как нам это сделать, мы должны всегда оставаться на одном или мы должны меняться по мере продолжения цепочки?
Вы хотите, чтобы некоторые вид (естественного) преобразования для переключения с IO
на Task
/ TaskEither
. Обратный путь для меня не имеет смысла, поскольку асин c эффект не может быть преобразован в син c.
chain
сохранит структуру . Так что getPage
в IO.chain(getPage)
нужна подпись number -> IO<whatever>
. Вместо этого вы можете использовать map
, чтобы добавить дополнительный слой вложенности, например:
pipe(getRnd(2, 4), IO.map(getPage)); // I.IO<TE.TaskEither<Error, string>>
В общем, нет правильного или неправильного пути, это просто зависит от цели. Обратите внимание: чем больше вложенных типов данных, тем сложнее будет обрабатывать внутренние значения. Часть функционального программирования с алгебраическими c структурами состоит в том, чтобы избежать ненужного вложения прямо в источник.
В вашем случае действительно имеет смысл объединить все в единую TaskEither
- у вас не будет никаких преимущество с типом IO<TaskEither<...>>
против TaskEither<...>
.
2.) Как просто заставить машинопись следовать за этими изменениями типов при переходе от одного к другому?
Вы можно использовать TaskEither.rightIO
для преобразования IO
в TaskEither
( CodeSandbox ):
import { taskEither as TE, io as I, option as O, pipeable as P, either as E } from "fp-ts";
declare function log<T>(t: T): I.IO<T>;
const getPageLinks = P.pipe(
getRnd(2, 4),
TE.rightIO,
TE.chain(getPage),
TE.map(getLinks),
TE.chain(v => TE.rightIO(log(v)))
); // TE.TaskEither<Error, O.Option<string[]>>
Это также работает, так как TS использует структурную типизацию ( но я бы порекомендовал прежний):
const getPageLinks2 = P.pipe(
getRnd(2, 4),
I.chain(getPage), // this also works
I.map(v => v.then(vv => E.either.map(vv, getLinks))),
I.chain(log)
); // I.IO<Promise<E.Either<Error, O.Option<string[]>>>>