1

我对函数式编程和特别是fp-ts库非常陌生。

我的问题包括两个部分:

  1. 我看到了一种将 Monads 从一种类型转换为另一种类型的模式,例如从TasktoIO或反之亦然,我们如何管理这一点,我们应该始终保持在一个类型上还是应该随着链条的继续而改变?
  2. 如何简单地使打字稿跟随这些从一个到另一个的类型更改?

例如,假设我们有几个函数,我们想将它们组合在一起,如下所示,我知道这个例子可能不是很实用,但它可以达到目的。

declare function getRnd(min: number, max: number): IO<number>; // Returns a random number within the range
declare function getPage(pageNo: number): TaskEither<Error, string>; // Make an Http request
declare function getLinks(pageContent: string): Option<string[]>; // Returns some links

// Let's say we wanna get a random page number and then return the links on it
// How do we compose these functions?
const getPageLinks = pipe(
  getRnd(2, 4),
  IO.chain(getPage), // I'm pretty sure TS will yells at me here
  TaskEither.chain(getLinks),
  log, // ?
)
4

1 回答 1

3

1.) 将 Monads 从一种类型转变为另一种类型,我们如何管理这一点,我们应该始终保持在一个类型上还是应该随着链条的继续而改变?

您想要某种(自然)转换从切换IOTask/ TaskEither。反过来对我来说没有意义,因为异步效果无法转换为同步效果。

chain保留结构。所以getPageinIO.chain(getPage)需要一个签名number -> IO<whatever>。您可以改为使用map添加额外的嵌套层,例如:

pipe(getRnd(2, 4), IO.map(getPage)); // I.IO<TE.TaskEither<Error, string>>

一般来说,没有对错之分,只取决于目的。请注意,嵌套的数据类型越多,处理内部值就越复杂。使用代数结构进行函数式编程的一部分是避免在源代码处进行不必要的嵌套。

在您的情况下,将所有内容整合到统一中确实很有意义TaskEither- 您不会对 type IO<TaskEither<...>>vs有任何优势TaskEither<...>

2.)如何简单地使打字稿跟随这些类型的变化从一个到另一个?

您可以使用TaskEither.rightIO转换IOTaskEitherCodeSandbox):

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[]>>>>
于 2020-03-18T20:59:56.813 回答