Closed mperktold closed 5 years ago
Hi, thank you for the suggestion!
In general, I see pluck
more as a legacy mechanism, with several downsides, like: you won't get completion, nor refactoring, nor go to definition in an IDE, and conceptually you now have two functions (pluck and map) doing the same thing and cluttering the API. I didn't look at the RxJS types, but I also suspect they only support up to a certain number of parameters for that function, with overloads (if they want to have it type-safe).
ideally you would have:
interface Nested<T> {
nested: Option<{ value: Option<T> }>;
}
In that case, you could do:
Option.some({nested: Option.of({value: Option.of("x")})
.flatMap(x => x.nested)
.flatMap(x => x.value)
With the original data structure, you'd have to do...
Option.some({nested: Option.of({value: Option.of("x")})
.flatMap(x => Option.of(x.nested))
.flatMap(x => Option.of(x.value))
Yes the pluck
solution looks more terse, but I don't think the compromises are worth it. I also never saw any language which would accept multiple lambdas for map
or flatMap
. Of course, some languages support operators and that's the perfect solution for that problem, like Option.some "x" |> value |> nested
, but we don't have those in JS...
Note that Option, like other prelude-ts data structures, offers an escape hatch, with transform. So if you'd like to have pluck
but I refuse it, you can always do =>
function myOptionPluck<T, U>(opt: Option<T>, accessor1: keyof T, ...): Option<U> {
...
}
someOption.transform(myOptionPluck("nested", "value"))
In general, I see
pluck
more as a legacy mechanism, with several downsides, like: you won't get completion, nor refactoring, nor go to definition in an IDE, and conceptually you now have two functions (pluck and map) doing the same thing and cluttering the API.
There is some support in Visual Studio Code for things like this, but you're right, it's limited. Your point regarding cluttering the API is also valid.
I didn't look at the RxJS types, but I also suspect they only support up to a certain number of parameters for that function, with overloads (if they want to have it type-safe).
Yes, that's exactly how it works.
ideally you would have:
interface Nested<T> { nested: Option<{ value: Option<T> }>; }
In that case, you could do:
Option.some({nested: Option.of({value: Option.of("x")}) .flatMap(x => x.nested) .flatMap(x => x.value)
With the original data structure, you'd have to do...
Option.some({nested: Option.of({value: Option.of("x")}) .flatMap(x => Option.of(x.nested)) .flatMap(x => Option.of(x.value))
In our project, we currently use our own Option
(actually called Maybe
) implementation for pluck
ing nested properties out of JSON objects coming from a remote API, whose structure is described by an interface.
Obviously, those objects cannot contain instances of Option
, but your second proposal would work indeed.
Note that Option, like other prelude-ts data structures, offers an escape hatch, with transform. So if you'd like to have
pluck
but I refuse it, you can always do =>function myOptionPluck<T, U>(opt: Option<T>, accessor1: keyof T, ...): Option<U> { ... } someOption.transform(myOptionPluck("nested", "value"))
Yes, this is a nice feature!
I also thought about something similar using a higher-order plucker
function together with flatMap
:
function plucker<T, K1 extends keyof T, K2 extends keyof T[K1]>
(k1: K1, k2: K2): (obj: T) => Option<T[K1][K2]>
{
...
}
someOption.flatMap(plucker("nested", "value"))
I opened this issue because when I saw this project, I thought about replacing our own implementation with this, and then a pluck
method similar to ours would be convenient.
But I agree that this is not needed in the Option
API. It can be achieved with simple custom functions, which compose nicely with the existing API. 👌
In RxJS, there is a
pluck
operator for mapping items to nested properties, specified by strings.I think this would nicely fit into
Option
, so we could write code like this:Ideally, we should get compiler errors if
pluck
is passed a string that isn't a valid property of the wrapped type, like in the last example. See the overloads ofpluck
in RxJS for inspiration how this can be done.