繁体   English   中英

使用 fp-ts sequenceT 时如何“加宽”阅读器类型?

[英]How to "widen" reader type when using fp-ts sequenceT?

我想知道在使用 sequenceT 时是否可以“扩大”我的最终 Reader 类型? 这在使用 chainW 等按顺序链接操作时是可能的,但看起来在使用 sequenceT 时,您必须使用相同的 Reader 类型的每个项目。 我想这样做,以便我能够在适当的情况下并行执行某些任务,但仍然能够通过 Reader 使用依赖项注入。

例子:

import { sequenceT } from 'fp-ts/lib/Apply'
import { log } from 'fp-ts/lib/Console'
import { pipe } from 'fp-ts/lib/function'
import * as RT from 'fp-ts/ReaderTask'

interface Person {
  name: string
}

const getMe = (name: string) => (deps: { api: any }) => async (): Promise<Person> => {
  const person = {
    name,
  }
  return person
}

const getMum = (child: Person) => (deps: { api2: any }) => async (): Promise<Person> => {
  const person = {
    name: child.name + "'s mum",
  }
  return person
}

const getDad = (child: Person) => (deps: { api2: any }) => async (): Promise<Person> => {
  const person = {
    name: child.name + "'s dad",
  }
  return person
}

const getFamily = (name: string) => 
  pipe(
    getMe(name),
    RT.chainW(me => 
      sequenceT(RT.readerTask)(
        getMum(me), 
        getDad(me))
    ))

getFamily('John')({ api: 'x', api2: 'y' })().then(
  ([mum, dad]) => {
    log(mum)()
    log(dad)()
  })

这编译得很好并输出:

$ node dist/src/index.js
{ name: "John's mum" }
{ name: "John's dad" }

现在假设 getDad 依赖于不同的 api,比如 api3。 如果我更新它不再编译的代码,因为 getMum 和 getDad 没有使用相同的 Reader 类型。

示例(不编译):

import { sequenceT } from 'fp-ts/lib/Apply'
import { log } from 'fp-ts/lib/Console'
import { pipe } from 'fp-ts/lib/function'
import * as RT from 'fp-ts/ReaderTask'

interface Person {
  name: string
}

const getMe = (name: string) => (deps: { api: any }) => async (): Promise<Person> => {
  const person = {
    name,
  }
  return person
}

const getMum = (child: Person) => (deps: { api2: any }) => async (): Promise<Person> => {
  const person = {
    name: child.name + "'s mum",
  }
  return person
}

const getDad = (child: Person) => (deps: { api3: any }) => async (): Promise<Person> => {
  const person = {
    name: child.name + "'s dad",
  }
  return person
}

const getFamily = (name: string) => 
  pipe(
    getMe(name),
    RT.chainW(me => 
      sequenceT(RT.readerTask)(
        getMum(me), // compiler complains here
        getDad(me))
    ))

getFamily('John')({ api: 'x', api2: 'y', api3: 'z' })().then( // compiler complains here, on api3
  ([mum, dad]) => {
    log(mum)()
    log(dad)()
  })

我实际上是用 StateReaderTaskEither 尝试这个,但在这个例子中简化了它以使用 ReaderTask - 然而,sequenceT 也表现出相同的限制。

任何想法如何解决?

这正是Reader/ReaderTask/ReaderTaskEither.local的用途! 我经常使用这个。 例如,如果您将 HTTP 调用并行化到 API,其中一些需要身份验证令牌 + 基本 URL,而其他人只需要基本 URL(因此有些使用interface Auth { token:string, baseUrl: string }而其他使用interface NoAuth { baseUrl: string }

  interface Apis {
    java: JavaRepository,
    db: DbRepository,
    redis: RedisRepository,
  }
  interface DomainError {}

  declare const javaApiCall: RTE<JavaRepository, DomainError, JavaResult>
  declare const dbApiCall: RTE<DbRepository, DomainError, DbResult>
  declare const redisApiCall: RTE<RedisRepository, DomainError, RedisResult>
  declare const apis: Apis

  const getJava = (apis:Apis) => apis.java
  const getDb = (apis:Apis) => apis.db
  const getRedis = (apis:Apis) => apis.redis

  sequenceT(readerTaskEither)(
    RTE.local(getJava)(javaApiCall),
    RTE.local(getDb)(dbApiCall),
    RTE.local(getRedix)(redisApiCall),
  )(apis) // TaskEither<DomainError, [JavaResult,DbResult,RedisResult]>

在阅读了一些 fp-ts 代码后,我发现了这一点。 我想出的答案是只做sequenceT手动有效地做的事情。

这是我的解决方案:

import { log } from 'fp-ts/lib/Console'
import { pipe } from 'fp-ts/lib/function'
import * as RT from 'fp-ts/ReaderTask'

interface Person {
  name: string
}

const getMe = (name: string) => (deps: { api: any }) => async (): Promise<Person> => {
  const person = {
    name,
  }
  return person
}

const getMum = (child: Person) => (deps: { api2: any }) => async (): Promise<Person> => {
  const person = {
    name: child.name + "'s mum",
  }
  return person
}

const getDad = (child: Person) => (deps: { api3: any }) => async (): Promise<Person> => {
  const person = {
    name: child.name + "'s dad",
  }
  return person
}

const getFamily = (name: string) => 
  pipe(
    getMe(name),
    RT.chainW(me => 
      pipe(
        RT.of((mum: Person) => (dad: Person) => [mum, dad]),
        RT.apW(getMum(me)),
        RT.apW(getDad(me))
      )
    ))

getFamily('John')({ api: 'x', api2: 'y', api3: 'z' })().then(
  ([mum, dad]) => {
    log(mum)()
    log(dad)()
  })

暂无
暂无

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

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