Typescript 如何";加宽“;使用fp ts sequenceT时的读卡器类型?

Typescript 如何";加宽“;使用fp ts sequenceT时的读卡器类型?,typescript,functional-programming,fp-ts,Typescript,Functional Programming,Fp Ts,我想知道在使用sequenceT时是否有可能“拓宽”我的最终阅读器类型?当使用CHAINEW等按顺序链接操作时,这是可能的,但当使用SEQUENCENT时,似乎您必须使用相同的读卡器类型来处理每个项目。我希望这样做,以便能够在适当的情况下并行执行某些任务,但仍然能够通过Reader使用依赖项注入 例如: import { sequenceT } from 'fp-ts/lib/Apply' import { log } from 'fp-ts/lib/Console' import { pipe

我想知道在使用sequenceT时是否有可能“拓宽”我的最终阅读器类型?当使用CHAINEW等按顺序链接操作时,这是可能的,但当使用SEQUENCENT时,似乎您必须使用相同的读卡器类型来处理每个项目。我希望这样做,以便能够在适当的情况下并行执行某些任务,但仍然能够通过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)()
  })
现在让我们假设getDad依赖于不同的api,比如api3。如果我更新代码,它将不再编译,因为getMum和getDad使用的读取器类型不同

示例(不编译):

从'fp ts/lib/Apply'导入{sequenceT}
从“fp ts/lib/Console”导入{log}
从“fp ts/lib/function”导入{pipe}
从“fp ts/ReaderTask”导入*作为RT
接口人{
名称:string
}
const getMe=(name:string)=>(deps:{api:any})=>async():Promise=>{
const person={
名称
}
返回人
}
const getMum=(child:Person)=>(deps:{api2:any})=>async():Promise=>{
const person={
name:child.name+“‘妈妈’”,
}
返回人
}
const getDad=(child:Person)=>(deps:{api3:any})=>async():Promise=>{
const person={
姓名:child.name+““的爸爸”,
}
返回人
}
const getFamily=(名称:string)=>
烟斗(
getMe(姓名),
RT.chainW(me=>
sequenceT(RT.readerTask)(
getMum(me),//编译器在这里抱怨
格达德(我)
))
getFamily('John')({api:'x',api2:'y',api3:'z'})()然后(//编译器在这里抱怨,在api3上
([妈妈,爸爸]=>{
日志(mum)()
日志(dad)()
})
实际上,我在StateReaderTaskEither中尝试了这个方法,但是在这个例子中,我简化了它,使用ReaderTask——sequenceT也展示了同样的限制


有什么办法可以解决吗?

我在多读了一点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)()
  })
从'fp ts/lib/Console'导入{log}
从“fp ts/lib/function”导入{pipe}
从“fp ts/ReaderTask”导入*作为RT
接口人{
名称:string
}
const getMe=(name:string)=>(deps:{api:any})=>async():Promise=>{
const person={
名称
}
返回人
}
const getMum=(child:Person)=>(deps:{api2:any})=>async():Promise=>{
const person={
name:child.name+“‘妈妈’”,
}
返回人
}
const getDad=(child:Person)=>(deps:{api3:any})=>async():Promise=>{
const person={
姓名:child.name+““的爸爸”,
}
返回人
}
const getFamily=(名称:string)=>
烟斗(
getMe(姓名),
RT.chainW(me=>
烟斗(
(妈妈:人)=>(爸爸:人)=>[妈妈,爸爸],
RT.apW(getMum(me)),
RT.apW(getDad(me))
)
))
getFamily('John')({api:x',api2:y',api3:z'})然后(
([妈妈,爸爸]=>{
日志(mum)()
日志(dad)()
})

这正是
Reader/ReaderTask/ReaderTask.local
的用途!我经常用这个。例如,如果您正在并行化对API的HTTP调用,其中一些API需要auth令牌+基本URL,而其他API只需要基本URL(因此一些API使用
接口auth{token:string,baseUrl:string}
,而其他API使用
接口NoAuth{baseUrl:string}

接口API{
java:JavaRepository,
db:DbRepository,
redis:redis存储库,
}
接口域错误{}
声明常量javaApiCall:RTE
声明常量dbapcall:RTE
声明常量:RTE
声明常量API:API
const getJava=(api:api)=>api.java
const getDb=(api:api)=>api.db
const getRedis=(api:api)=>api.redis
sequenceT(读取器塔斯基瑟)(
RTE.local(getJava)(javaApiCall),
RTE.本地(getDb)(DBAPCALL),
RTE.本地(getRedix)(全部),
)(API)//task或

甚至更好!谢谢你的强调。Fp ts很棒,但它有点像寻宝游戏,因为它缺乏实用的文档。
Fp ts
当然假设人们对Fp的熟悉程度在他们第一次接触它时可能没有。我很幸运我用Kotlin的Arrow在Fp中切牙,所以我对事情很熟悉就像一个读者的
local
和它的用途。我记得当我第一次在科特林使用它时,直到有一天我看到它被使用,才知道它到底是用来干什么的。
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)()
  })

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)()
  })