fp-ts
fp-ts copied to clipboard
sequenceS, but for single properties
🚀 Feature request
Current Behavior
None
Desired Behavior
I don't have the terminology, I'll try to be as clear as possible
I find myself with an object with a monad inside it, I need to expand the effect of such monad to the whole monad
Let's call it sequenceKey
const obj = {
a: O.some(1),
b: O.none,
c: true,
}
const res1 = sequenceKey("a", O.Applicative)(obj) // O.some({ a: 1, b: O.none, c: true })
const res2 = sequenceKey("b", O.Applicative)(obj) // O.none
Suggested Solution
Basically something like this
export function sequenceKey<P extends string, F extends URIS4>(
prop: P, F: Apply4<F>
): <S, R, E, NER extends Record<P, Kind4<F, S, R, E, any>>>(
r: NER & Record<P, Kind4<F, S, R, E, any>>
) => Kind4<F, S, R, E, { [K in keyof NER]: K extends P ? ([NER[K]] extends [Kind4<F, any, any, any, infer A>] ? A : never) : NER[K] }>
export function sequenceKey<P extends string, F extends URIS3>(
prop: P, F: Apply3<F>
): <R, E, NER extends Record<P, Kind3<F, R, E, any>>>(
r: NER & Record<P, Kind3<F, R, E, any>>
) => Kind3<F, R, E, { [K in keyof NER]: K extends P ? ([NER[K]] extends [Kind3<F, any, any, infer A>] ? A : never) : NER[K] }>
export function sequenceKey<P extends string, F extends URIS3, E>(
prop: P, F: Apply3C<F, E>
): <R, NER extends Record<P, Kind3<F, R, E, any>>>(
r: NER & Record<P, Kind3<F, R, E, any>>
) => Kind3<F, R, E, { [K in keyof NER]: K extends P ? ([NER[K]] extends [Kind3<F, any, any, infer A>] ? A : never) : NER[K] }>
export function sequenceKey<P extends string, F extends URIS2>(
prop: P, F: Apply2<F>
): <E, NER extends Record<P, Kind2<F, E, any>>>(
r: NER & Record<P, Kind2<F, E, any>>
) => Kind2<F, E, { [K in keyof NER]: K extends P ? ([NER[K]] extends [Kind2<F, any, infer A>] ? A : never) : NER[K] }>
export function sequenceKey<P extends string, F extends URIS2, E>(
prop: P, F: Apply2C<F, E>
): <NER extends Record<P, Kind2<F, E, any>>>(
r: NER
) => Kind2<F, E, { [K in keyof NER]: K extends P ? ([NER[K]] extends [Kind2<F, any, infer A>] ? A : never) : NER[K] }>
export function sequenceKey<P extends string, F extends URIS>(
prop: P, F: Apply1<F>
): <NER extends Record<P, Kind<F, any>>>(
r: NER
) => Kind<F, { [K in keyof NER]: K extends P ? ([NER[K]] extends [Kind<F, infer A>] ? A : never) : NER[K] }>
export function sequenceKey<P extends string, F>(
prop: P, F: Apply<F>
): <NER extends Record<P, HKT<F, any>>>(
r: NER
) => HKT<F, { [K in keyof NER]: K extends P ? ([NER[K]] extends [HKT<F, infer A>] ? A : never) : NER[K] }>
export function sequenceKey<P extends string, F>(prop: P, F: Apply<F>) {
return (obj: Record<P, HKT<F, any>>) => F.map(obj[prop], (value) => ({ ...obj, [prop]: value }))
}
And the potential traverseKey
, for which I still have to find the type
There is also a fork https://github.com/DrTtnk/fp-ts
Who does this impact? Who is this for?
From beginners and above, every time there are consideration to make for a whole object, given a single property
Describe alternatives you've considered
Using boilerplate code with pipe
, map
, object destructuring... That has to be reimplemented in case there were other monadic types to manage
const obj = {
a: O.some(1),
b: "a",
c: O.none
}
const result = pipe(
obj,
({a, ...base}) => pipe(
a,
O.map((a) => ({...base, a}))
)
)
Your environment
Software | Version(s) |
---|---|
fp-ts | 2.21.1 |
TypeScript | 4.7.4 |
The code you have posted implies that there is some computation down the road where b and c depend on a. I think the do notation using apS and bindS works well for such cases and clearly shows the intent.
I wish I could do something like this:
const test = {
a: 'a',
b: 'b',
c: O.some('c'),
}
const res = pipe(
O.some(test),
O.bind('c', (c) => O.some(c)),
// ^^^ TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
)
Unfortunately this is a typeerror
In that case, what about
sequenceS(O.Applicative)({a:O.some(obj.a), b:O.some(obj.b),c})
Ultimately, that data structure has to go through some sort of transformation like that since as it stands(and I assume thats due to upstream api or whatever) it is sort of smelly. I think values in a product type should not have dependency on each other like this.