简体   繁体   English

在 fp-ts 中合并 2 个 TaskEither

[英]merging 2 TaskEither in fp-ts

I am working on a project that using fp-ts我正在做一个使用fp-ts的项目

I have 2 TaskEither object like TaskEither<ErrorA, A>, TaskEither<ErrorB, B>我有 2 TaskEither object 像 TaskEither<ErrorA, A>, TaskEither<ErrorB, B>

I wanted to merging this objects contents and create new TaskEither<ErrorA, A>,我想合并这个对象的内容并创建新的 TaskEither<ErrorA, A>,
Example A object = {a: 123, b: 456, c: 0}

Example B object = {c: 789}

I wanted to create newObject: TaskEither<ErrorA, A>我想创建newObject: TaskEither<ErrorA, A>

and If everything goes well, expecting value should be {a: 123, b: 456, c: 789 }如果一切顺利,期望值应该是{a: 123, b: 456, c: 789 }

I would recommend to use the Do-Notation in this case.在这种情况下,我建议使用 Do-Notation。 Here an example.这里举个例子。

import { pipe } from 'fp-ts/function'
import * as TE from 'fp-ts/TaskEither'

interface A {
  a: number
  b: number
  c: number
}

interface B {
  c: number
}

const a = TE.right<Error, A>({ a: 123, b: 456, c: 0 })
const b = TE.right<Error, B>({ c: 789 })

const c: TE.TaskEither<Error, A> = pipe(
  TE.Do,
  TE.apS('a', a),
  TE.apS('b', b),
  TE.map(({ a, b }) => ({ ...a, ...b }))
)

If the error types are not the same you should consider to wrap your errors in a union type.如果错误类型不同,您应该考虑将错误包装在联合类型中。 There is a longer thread about that in the fp-ts Issue Tracker .fp-ts 问题跟踪器中有一个更长的线程。

Using TE.Do is of course a solution (clean and small).使用 TE.Do 当然是一种解决方案(干净且小巧)。 For another usage, you can just chain your tasks usings pipes.对于另一种用法,您可以使用管道链接您的任务。

export const a = (): TE.TaskEither<ErrorA, A> => {
  return TE.right({ a: 123, b: 456, c: 0 });
}

export const b = (): TE.TaskEither<ErrorB, B> => {
  return TE.right({ c: 789 });
}

export const c =(): TE.TaskEither<ErrorA, A> => {
  return pipe(
    a(),
    TE.chain(a => {
      return pipe(
        b(),
        TE.map(b => {
          return {
            ...a,
            c: b.c,
          }
        }),
      );
    }),
  );
}

If you're working with all things of the same applicative (and recall all monads are applicatives), the most straightforward way is:如果您正在处理相同应用程序的所有事物(并且回想所有 monad 都是应用程序),最直接的方法是:

type A = { /*...*/ }
type B = { /*...*/ }

declare const a: TaskEither<string, A>
declare const b: TaskEither<string, B>

sequenceS(TE.ApplyPar)({ a, b }) // TaskEither<string, { a: A, b: B }>; could also use TE.ApplySeq instead for sequential instead of parallel processing of async calls

the TaskEither will produce left if either a or b is a left.如果ab是 left,则TaskEither将产生 left。

The sequence variations (there are three, Apply.sequenceS, Apply.sequenceT, and Array.sequence) specifically exist to do what you are asking: take a record/array of monads and result in a record/array of their results if they all "succeed". sequence变体(共有三个,Apply.sequenceS、Apply.sequenceT 和 Array.sequence)专门用于执行您所要求的操作:获取单子的记录/数组,如果它们都存在,则生成其结果的记录/数组“成功”。 Otherwise you get a single "failure."否则你会得到一个单一的“失败”。

declare const firstOpt: () => Option<string>
declare const secondOpt: () => Option<number>
sequenceT(Option.Apply)(firstOpt(), secondOpt())
// None or Option<[string, number]>

declare const firstEither: () => Left<string>
declare const secondEither: () => Right<number>
sequenceS(Either.Apply)({
  first: firstEither(),
  second: secondEither(),
})
// Left<string>

etc.等等

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM