【问题标题】:(Resolved) How to chain dependent TaskEither operations in FP-TS(已解决)如何在 FP-TS 中链接依赖的 TaskEither 操作
【发布时间】:2019-08-01 20:55:13
【问题描述】:

我是 FP-TS 的新手,但仍然不太了解如何使用 TaskEither。我正在尝试异步读取文件,然后使用 yaml-parse-promise 解析生成的字符串。

==编辑==

我使用文件的完整内容更新了代码以提供更多上下文,并应用了 MnZrK 提供的一些建议。抱歉,我还是 FP-TS 的新手,我仍在努力让类型匹配。

现在我的错误在于map(printConfig) 行:

Argument of type '<E>(fa: TaskEither<E, AppConfig>) => TaskEither<E, AppConfig>' is not assignable to parameter of type '(a: TaskEither<unknown, AppConfig>) => Either<unknown, Task<any>>'.
  Type 'TaskEither<unknown, AppConfig>' is not assignable to type 'Either<unknown, Task<any>>'.
    Type 'TaskEither<unknown, AppConfig>' is missing the following properties from type 'Right<Task<any>>': _tag, rightts(2345)

[我通过使用来自 TaskEither 而不是来自 Either 库的 getOrElse 解决了这个问题]

==结束编辑==

我已成功使用 IOEither 执行此操作,作为与此项目的同步操作:https://github.com/anotherhale/fp-ts_sync-example

我还查看了这里的示例代码: https://gcanti.github.io/fp-ts/recipes/async.html

完整代码在这里:https://github.com/anotherhale/fp-ts_async-example

import { pipe } from 'fp-ts/lib/pipeable'
import { TaskEither, tryCatch, chain, map, getOrElse } from "fp-ts/lib/TaskEither";
import * as T from 'fp-ts/lib/Task';
import { promises as fsPromises } from 'fs';
const yamlPromise = require('js-yaml-promise');

// const path = require('path');
export interface AppConfig {
  service: {
    interface: string
    port: number
  };
}

function readFileAsyncAsTaskEither(path: string): TaskEither<unknown, string> {
  return tryCatch(() => fsPromises.readFile(path, 'utf8'), e => e)
}

function readYamlAsTaskEither(content: string): TaskEither<unknown, AppConfig> {
  return tryCatch(() => yamlPromise.safeLoad(content), e => e)
}

// function getConf(filePath:string){
//   return pipe(
//       readFileAsyncAsTaskEither(filePath)()).then(
//           file=>pipe(file,foldE(
//               e=>left(e),
//               r=>right(readYamlAsTaskEither(r)().then(yaml=>
//                   pipe(yaml,foldE(
//                       e=>left(e),
//                       c=>right(c)
//                   ))
//               ).catch(e=>left(e)))
//           ))
//       ).catch(e=>left(e))
// }

function getConf(filePath: string): TaskEither<unknown, AppConfig> {
  return pipe(
    readFileAsyncAsTaskEither(filePath),
    chain(readYamlAsTaskEither)
  )
}

function printConfig(config: AppConfig): AppConfig {
  console.log("AppConfig is: ", config);
  return config;
}

async function main(filePath: string): Promise<void> {
  const program: T.Task<void> = pipe(
    getConf(filePath),
    map(printConfig),
    getOrElse(e => {
      return T.of(undefined);
    })
  );

  await program();
}

main('./app-config.yaml')

结果输出是: { _tag: 'Right', right: Promise { &lt;pending&gt; } }

但我想要生成的 AppConfig: { service: { interface: '127.0.0.1', port: 9090 } }

【问题讨论】:

    标签: fp-ts


    【解决方案1】:

    所有这些e=&gt;left(e).catch(e=&gt;left(e)) 都是不必要的。 您的第二种方法更惯用。

    // convert nodejs-callback-style function to function returning TaskEither
    const readFile = taskify(fs.readFile);
    // I don't think there is `taskify` alternative for Promise-returning functions but you can write it yourself quite easily
    const readYamlAsTaskEither = r => tryCatch(() => readYaml(r), e => e);
    
    function getConf(filePath: string): TaskEither<unknown, AppConfig> {
      return pipe(
        readFile(path.resolve(filePath)),
        chain(readYamlAsTaskEither)
      );
    }
    

    现在您的getConf 返回TaskEither&lt;unknown, AppConfig&gt;,它实际上是() =&gt; Promise&lt;Either&lt;unknown, AppConfig&gt;&gt;。如果您有比unknown 更具体的错误类型,请改用它。

    为了“解包”实际值,您需要有一些主入口点功能,您需要使用mapchain(即打印到控制台)来编写您需要对配置执行的其他内容,然后应用一些错误处理来摆脱Either 部分,最后得到Task(实际上只是懒惰() =&gt; Promise):

    import * as T from 'fp-ts/lib/Task';
    
    function printConfig(config: AppConfig): AppConfig {
      console.log("AppConfig is", config);
      return config;
    }
    
    function doSomethingElseWithYourConfig(config: AppConfig): TaskEither<unknown, void> {
      // ...
    }
    
    async function main(filePath: string): Promise<void> {
      const program: T.Task<void> = pipe(
        getConf(filePath),
        map(printConfig),
        chain(doSomethingElseWithYourConfig),
        // getting rid of `Either` by using `getOrElse` or `fold`
        getOrElse(e => {
          // error handling (putting it to the console, sending to sentry.io, whatever is needed for you app)
          // ...
          return T.of(undefined);
        })
      );
    
      await program();
    }
    

    【讨论】:

    • 我使用的是来自 Either 而不是来自 TaskEither 的 getOrElse。在我更新以使用正确的库后,它现在可以工作了。感谢您的反馈@MnZrK
    猜你喜欢
    • 2022-01-07
    • 2022-07-19
    • 1970-01-01
    • 2020-08-16
    • 1970-01-01
    • 2021-07-17
    • 1970-01-01
    • 2012-02-16
    • 1970-01-01
    相关资源
    最近更新 更多