使用fp-ts sequenceT时如何"widen" reader打字?
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 也表现出相同的限制。
有什么解决办法吗?
我在多读了一些 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)()
})
这正是 Reader/ReaderTask/ReaderTaskEither.local
的用途!我经常使用这个。例如,如果您正在并行化对 API 的 HTTP 调用,其中一些需要 auth token + base URL,而另一些只需要 base 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]>
我想知道在使用 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 也表现出相同的限制。
有什么解决办法吗?
我在多读了一些 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)()
})
这正是 Reader/ReaderTask/ReaderTaskEither.local
的用途!我经常使用这个。例如,如果您正在并行化对 API 的 HTTP 调用,其中一些需要 auth token + base URL,而另一些只需要 base 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]>