首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >fp:我如何将嵌套的‘I’/‘ts’“拉”到外部类型?

fp:我如何将嵌套的‘I’/‘ts’“拉”到外部类型?
EN

Stack Overflow用户
提问于 2021-04-23 11:37:04
回答 2查看 785关注 0票数 1

我正在学一些fp-ts。要创建我正在处理的问题的样式化版本,假设我想要创建一个不存在的表,所以我必须查询数据库:一个易出错的异步操作。如果该表不存在,我希望创建它:另一个错误的异步操作。进一步假设错误类型都是字符串(如果需要的话,我还想知道如何创建一个联合错误类型),并且成功创建时返回的值是一个数字ID。

简而言之,看看表是否在那里,如果没有,就创建它--在整个过程中都有出错的可能。关键是我希望两个错误都反映在最外层的类型:一个TaskEither<string, Option<number>>中。问题是,我不知道如何避免获得TaskEither<string, Option<TaskEither<string, number>>>。也就是说,我不知道将错误拉到Option内部并将其合并为最外层错误的最佳方法。

(也许这涉及序列或可遍历?我还在学习这些。)

关于一些代码:

代码语言:javascript
复制
import { taskEither as TE, option as O } from "fp-ts";
import { pipe } from "fp-ts/lib/function";

// tableExists: () => TE.TaskEither<string, boolean>
// createTable: () => TE.TaskEither<string, number>

// I want this to represent both possible errors. Currently a type error.
// -------------------------------vvvvvv
const example = (): TE.TaskEither<string, O.Option<number>> => {
  return pipe(
    tableExists(),
    // How to pull the possible `left` up to the outermost type?
    // ------------------------------------------vvvvvvvvvvvvv
    TE.map((exists) => (exists ? O.none : O.some(createTable()))
  );
};
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-04-26 20:51:37

我想我想明白了,当然欢迎任何更正。

与其将TaskEitherOption中“拉”出来,我认为我需要将Option“推”到嵌套的TaskEither中,以便嵌套将TaskEither的各层放在一起,从而允许通过chain将它们压平。

代码语言:javascript
复制
const example = (): TE.TaskEither<string, O.Option<number>> =>
  pipe(
    tableExists(),
    TE.chain((exists) =>
      exists
        ? TE.of(O.none)
        : pipe(
            createTable(),
            TE.map(O.of)
          )
    )
  );

如果错误类型不同,我会做些什么,这似乎也是由这段代码处理的,但TE.chainW代替了TE.chain

票数 1
EN

Stack Overflow用户

发布于 2021-04-26 21:13:34

似乎您自己解决了这个问题:)如果有帮助,我已经将错误实现为一个受歧视的联合,这样您就可以很容易地识别在调用example时发生了哪些错误。

代码语言:javascript
复制
import * as TE from 'fp-ts/lib/TaskEither'
import * as O from 'fp-ts/lib/Option'
import { pipe } from "fp-ts/lib/function";

declare const tableExists: () => TE.TaskEither<string, boolean>
declare const createTable: () => TE.TaskEither<string, number>

// Discriminated union so you can easily identify which error it is
type ExampleErr = { tag: "TableExistsError", error: unknown } | { tag: "CreateTableError", error: unknown }

const example = (): TE.TaskEither<ExampleErr, O.Option<number>> => {
  return pipe(
    tableExists(),
    TE.mapLeft(error => ({ tag: "TableExistsError" as const, error })),
    TE.chainW(exists => exists ?
      TE.right(O.none) :
      pipe(
        createTable(),
        TE.mapLeft(error => ({ tag: "CreateTableError" as const, error })),
        TE.map(O.some)
      )
    )
  );
};

如果来自chainWcreateTable的错误类型不同,您正确地识别了需要使用createTableWfp-ts中函数末尾的意思是“加宽”,它通常允许将类型扩大到两种类型的合并。对于chainW for TaskEither,这意味着错误类型将成为两个TaskEither类型(进入chainW的类型和在其中返回的类型)的联合。

理解何时使用map和何时使用chain是一个重要的基本概念,这对于很好地理解非常重要。map允许您修改结构中的值,它是来自A -> B的一个简单函数。chain允许您执行另一个依赖于第一个结果的效果,因此您必须返回一个值,该值由您所处理的相同效果包装。在本例中,您使用的是TaskEither,因此传递给chain的函数也需要类型为A -> TaskEither<E, B> ( createTable是,但也需要手动处理表已经存在的情况,并使用TE.right(O.none)TE.of(O.none)在那里构造TaskEither )。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67229271

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档