2

我正在使用 FP-TS 学习 FP,但遇到了障碍:

我的存储库中有以下功能:

// this is the repository
export const findBook = (id: string) => TaskEither<Error, Option<ParsedBook>> 

这部分很简单,对我来说很有意义。问题是当我尝试从我的控制器调用它时:

// this is the controller
export const getBook = (req: unknown) => Task<string | ParsedBook>

无论如何,这是我的getBook和我正在尝试做的事情:

const getBook: (req: unknown) => T.Task<string | ParsedBook> = flow(
  getBookByIdRequestV.decode, // returns Either<Errors, GetBookByIdRequest>
  E.fold(
    () => T.of('Bad request!'),
    flow(
      prop('params'),
      prop('id'),
      findBook, // returns TaskEither<Error, Option<ParsedBook>>
      TE.fold(
        () => T.of('Internal error.'),
        O.fold(
          () => T.of('Book not found.'),
          (book) => T.of(book) // this line results in an error
        )
      )
    )
  )
)

问题是上面的代码给了我一个错误:

Type 'ParsedBook' is not assignable to type 'string'

我认为问题在于E.fold期望onLeftonRight返回相同类型的结果:

fold<E, A, B>(onLeft: (e: E) => B, onRight: (a: A) => B): (ma: Either<E, A>) => B

但是,它可能不仅返回 a Task<string>,而且还返回 a Task<ParsedBook>

我尝试使用foldW,它扩大了类型,但同样的错误也是如此。

我真的不知道该怎么办;我觉得我在代码中建模类型的方式很糟糕?

如果有帮助,这里是 Codesandbox:https ://codesandbox.io/s/tender-chandrasekhar-5p2xm?file=/src/index.ts

谢谢!

4

2 回答 2

2

尝试使用时遇到的错误foldW是因为Task<string> | Task<ParsedBook>is not assignable to Task<string | ParsedBook>.

您可以使用with代替解开TaskEitherusingTE.fold然后重新包装 Task 中的值 using :T.ofT.mapE.foldW

import { identity } from "fp-ts/function";

T.map(E.foldW(
  () => "Internal error.",
  O.foldW(() => "Book not found.", identity)
))

事实上,有一种更简单的方法可以使用O.getOrElseW

T.map(E.foldW(
  () => "Internal error.",
  O.getOrElseW(() => "Book not found.")
))

完整代码:

const getBook: (req: unknown) => T.Task<string | ParsedBook> = flow(
  getBookByIdRequestV.decode,
  E.fold(
    () => T.of("Bad request!"),
    flow(
      prop("params"),
      prop("id"),
      findBook,
      T.map(E.foldW(
        () => "Internal error.",
        O.getOrElseW(() => "Book not found.")
      ))
    )
  )
)
于 2021-02-27T02:08:14.917 回答
1

很好,我正在尝试自己了解更多关于 FP 的信息。

我不确定我是否理解getBook应该做什么。但我会保留它的回报,TaskEither因为它应该代表失败的可能性。

在使用 Functors/Monads 时,我认为它们就像一个盒子。我想把我的价值观保存在盒子里。如果您查找它,fold您会发现它列在析构函数下,那是因为它会破坏您的盒子。所以避免fold,如果必须的话,也许在计算结束时这样做。

但是,如果我们的价值观被困在一个盒子里,我们该怎么做呢?map是我们在这里的第一个解决方案,因为我们可以采用一个作用于值的函数现在作用于盒子。但它使我们的价值观保持不变。

我们也可以用TE.fromEitheror替换 Box TE.fromOption,这样我们的值就会保存在它的盒子里。

最后我们可以chain只保留一个盒子。每次 amap会给我们一个盒子里的盒子,我们可以把它改成chain. 但是盒子的类型必须对齐,否则你必须先转换它。

考虑到这些,我会这样写getBook

const getBook2: (req: unknown) => TE.TaskEither<string, ParsedBook> = flow(
  getBookByIdRequestV.decode,
  E.mapLeft(() => 'Bad request!'),
  E.map(({ params }) => params.id),
  TE.fromEither,
  TE.chain(flow(
    findBook,
    TE.mapLeft(() => 'internal error.')
  )),
  TE.chain(TE.fromOption(() => 'Book not found.'))
)

希望这会有所帮助,我仍在学习自己。所以让我知道是否有更好的方法。

于 2021-02-23T22:11:57.037 回答