Skip to content

Removing resultSelectors (WIP)#3304

Merged
benlesh merged 10 commits intoReactiveX:masterfrom
benlesh:remove-resultSelectors-1
Mar 2, 2018
Merged

Removing resultSelectors (WIP)#3304
benlesh merged 10 commits intoReactiveX:masterfrom
benlesh:remove-resultSelectors-1

Conversation

@benlesh
Copy link
Member

@benlesh benlesh commented Feb 7, 2018

This is an experimental PR that is aiming to remove all superfluous resultSelector arguments from operators and observable creators.

To start with I've done mergeMap, mergeMapTo, switchMap, switchMapTo, concatMap, concatMapTo and forkJoin.

See commit messages for more detail.

This is aimed at smaller size, simplifying the API, simplifying the code base, and simplifying TypeScript typings.

Just doing it makes it clear that it's a solid decision, as the code cleans up substantially everywhere it's done.

Relates to #2929

@benlesh benlesh requested a review from kwonoj February 7, 2018 23:25
@rxjs-bot
Copy link

rxjs-bot commented Feb 7, 2018

Warnings
⚠️

❗ Big PR (1)

Messages
📖

(1) : Pull Request size seems relatively large. If Pull Request contains multiple changes, split each into separate PR will helps faster, easier review.

CJS: 1323.4KB, global: 704.6KB (gzipped: 115.4KB), min: 136.2KB (gzipped: 30.0KB)

Generated by 🚫 dangerJS

@coveralls
Copy link

coveralls commented Feb 7, 2018

Coverage Status

Coverage decreased (-0.04%) to 97.522% when pulling 08e8df5 on benlesh:remove-resultSelectors-1 into 52cdfe8 on ReactiveX:master.

@kwonoj
Copy link
Member

kwonoj commented Feb 8, 2018

@benlesh before further refactoring in code, please consider #3050 to land prior. it has been resolved conflict several time and waiting review - while functionally it doesn't change anything, it's blocking for TS user would like to use strictFn option which need to be resolved eventually.

@benlesh benlesh force-pushed the remove-resultSelectors-1 branch from 91b9dee to 99f21d4 Compare February 8, 2018 03:45
@benlesh
Copy link
Member Author

benlesh commented Feb 8, 2018

@kwonoj that's done.

@david-driscoll
Copy link
Member

So I wonder if there is a good way to deal with the result selector scenario other than just saying "tough luck".

Going from something like the below just feels worse from a consumers perspective. I totally get it from the authors perspective.

let a4: Rx.Observable<{ o: number; i: string; }> = o.mergeMap(x => [x.toString()], (o, i) => ({ o, i }), 3);

to

let a6: Rx.Observable<{ o: number; i: string; }> = o.map(o => Observable.from([o.toString()]).map(i => ({ o, i }))).mergeAll(3);

I wonder if there is somewhere we can meet in the middle... I'll see if I can figure something out.

@kwonoj
Copy link
Member

kwonoj commented Feb 20, 2018

all blocked by mono-repo roadmap, but what we discussed in general is probably considerable to have separate operator package for legacy behaviors for opt-in. It's aligned effort of making core slim, and opt in for other intended behaviors.

@benlesh
Copy link
Member Author

benlesh commented Feb 21, 2018

@david-driscoll the change would be more like:

o.mergeMap((o => Observable.from(a.toString()).map(i => ({o, i })), 3);

There's no need to use mergeAll

@luillyfe
Copy link
Contributor

why are they superfluous ? Actually I can find ResultSelector pretty useful, because I can get access to the index of the source observable and inner observable. Also I can make a custom merge of the observables since I can get access to each item produce in each observable. Is there any place where I can get more info about this topic ?

@david-driscoll
Copy link
Member

@luillyfe The reason they're seen as superfluous is due to the fact they complicate the inner workings of each operator they're running inside, where it causes problems unit testing all the parts of the operator.

the result selector can easily substituted for something like...

.mergeMap((outer, outerIndex) => Observable.from(outer).map((inner, innerIndex) => ...));

However I agree that is pretty verbose and over the top, so I have a different PR where I'm proposing a helper method to help with the transition (and ensure consistency) #3332

// <type imports here>
import { map } from '../operators/map';
import { from } from '../observable/from';

export function over<O, I, R>(
  selector: (outer: O, index: number) => ObservableInput<I>,
  project: (outerValue: O, innerValue: I, outerIndex: number, innerIndex: number) => R): (outer: O, index: number) => Observable<R> {
  return (outer, outerIndex) => from(selector(outer, outerIndex))
    .pipe(map((inner, innerIndex) => project(outer, inner, outerIndex, innerIndex)));
}

the usage is pretty simple...

.mergeMap(over((outer, inner, outerIndex, innerIndex) => { ... }));

The method is fairly simple but I'd hate to force everyone that wants to use it to define it, so I'll be bringing this up in the next meeting.

@benlesh
Copy link
Member Author

benlesh commented Feb 28, 2018

@luillyfe they're superfluous because the following is equivalent:

source$.pipe(
  mergeMap((a, i) => of(a + i), (a, b, i, ii) => a + b + i + ii),
)

is the same as:

source$.pipe(
  mergeMap((a, i) => of(a + i).pipe(map((b, ii) => a + b + i + ii)))
)

Only the latter is actually better in many cases, as it allows for catching errors before they propagate out of the mergeMap operation.

The added benefit here is size, as almost always people will be using the map operator, it's generally no extra cost, where we're removing several lines of code from the mergeMap operator.

@benlesh benlesh force-pushed the remove-resultSelectors-1 branch from 7a33063 to de57152 Compare February 28, 2018 22:53
@luillyfe
Copy link
Contributor

luillyfe commented Mar 1, 2018

Awesome, thank you guys @benlesh @david-driscoll for the clarification.

benlesh added 7 commits March 1, 2018 16:25
BREAKING CHANGE: mergeMap, concatMap and concatMapTo no longer support a result selector, if you need to use a result selector, use the following pattern: `source.mergeMap(x => of(x + x).pipe(map(y => y + x))` (the pattern would be the same for `concatMap`).
- Implements mergeMapTo in terms of mergeMap
- Removes resultSelector argument

BREAKING CHANGE: `mergeMapTo` no longer accepts a resultSelector, to get this functionality, you'll want to use `mergeMap` and `map` together: `source.pipe(mergeMap(() => inner).pipe(map(y => x + y)))`
- removes resultSelector from forkJoin
- updates tests
- removes resultSelector argument from `switchMap` and `switchMapTo`
- updates tests

BREAKING CHANGE: `switchMap` and `switchMapTo` no longer take `resultSelector` arguments, to get the same functionality use `switchMap` and `map` in combination: `source.pipe(switchMap(x => of(x + x).pipe(y => x + y)))`.
- Removes resultSelector argument
- updates tests

BREAKING CHANGE: `resultSelector` no longer supported, to get this functionality use: `source.pipe(exhaustMap(x => of(x + x).pipe(map(y => x + y))))`
- removes resultSelector argument
- updates tests

BREAKING CHANGE no longer supports `resultSelector` argument. The same functionality can be achieved by simply mapping either before or after `first` depending on your use case.
- Removes resultSelector argument
- Updates tests

BREAKING CHANGE: no longer accepts `resultSelector` argument. To get this same functionality, use `map`.
@benlesh benlesh force-pushed the remove-resultSelectors-1 branch from de57152 to 193ff36 Compare March 2, 2018 00:25
expectSubscriptions(e1.subscriptions).toBe(e1subs);
});

<<<<<<< HEAD
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benlesh whoops

});

=======
>>>>>>> feat(mergeMap|concatMap|concatMapTo): simplified the signatures
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops

- Also fixes issues from a bad rebase that I (@benlesh) did because I'm
a dope.
@benlesh benlesh force-pushed the remove-resultSelectors-1 branch from 193ff36 to 7ff121c Compare March 2, 2018 02:05

function project() { return inner; }
function resultSelector(oV: string, iV: string, oI: number, iI: number) { return iV; }
const result = e1.mergeMap(project, resultSelector, 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like these tests shouldn't have been removed, instead just removing the usage of resultSelector


function project() { return inner; }
function resultSelector(oV: string, iV: string, oI: number, iI: number) { return iV; }
const result = e1.mergeMap(project, resultSelector, 2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto


function project(x: string) { return inners[x]; }
function resultSelector(oV: string, iV: string, oI: number, iI: number) { return iV; }
const result = e1.mergeMap(project, resultSelector, 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto


function project(x: string) { return inners[x]; }
function resultSelector(oV: string, iV: string, oI: number, iI: number) { return iV; }
const result = e1.mergeMap(project, resultSelector, 2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

' ^ !'];
const expected = '-----i---j---k---l-------i---j---k---l-------i---j---k---l---|';

function resultSelector(oV: string, iV: string, oI: number, iI: number) { return iV; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exists below "without resultSelector". I refactored to use the pipeable operator anyhow.

' ^ !'];
const expected = '-----i---j---(ki)(lj)k---(li)j---k---l---|';

function resultSelector(oV: string, iV: string, oI: number, iI: number) { return iV; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... yeah, there's the same test below it "without resultSelector"

benlesh added 2 commits March 1, 2018 20:17
- also readds important tests removed in a previous commit in this PR
@benlesh benlesh merged commit 9319d9b into ReactiveX:master Mar 2, 2018
@benlesh
Copy link
Member Author

benlesh commented Mar 2, 2018

Thanks for the review @jayphelps!

Note to future viewers of this PR: The red "X" was only because code coverage dropped 0.03% (or something like that)... So it's acceptable and merged anyhow.

@lock
Copy link

lock bot commented Jun 6, 2018

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Jun 6, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants