Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace mergemap with switchmap #30

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

raveclassic
Copy link
Contributor

@raveclassic raveclassic commented Apr 8, 2020

This PR replaces mergeMap with switchMap to avoid memory leaks.

contains parts of #29

closes #27

Observable does not always satisfy Alternative's distributivity law
BREAKING CHANGE: chain now unsubscribes from passed observable on source emit
URI,
map: (fa, f) => fa.pipe(rxMap(f)),
of,
ap: (fab, fa) => combineLatest([fab, fa]).pipe(rxMap(([f, a]) => f(a))),
chain: (fa, f) => fa.pipe(mergeMap(f)),
chain: (fa, f) => fa.pipe(switchMap(f)),
Copy link
Collaborator

@mlegenhausen mlegenhausen Apr 9, 2020

Choose a reason for hiding this comment

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

Theoretical we could replace switchMap with exhaustMap and would also get the memory problem solved and hold all laws right?

In practice you are totally right that switchMap should be the default chain. Should we add for completeness alternative Monad instances like mergeMonad and exhaustMonad?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

AFAIK exhaustMap waits for passed Observable to complete before subscribing to the new created one (result of chain). Looks like an incorrect behavior and I can't think of a use case for that.

Replacing switchMap with mergeMap is a matter of const m = {...observable, chain: (fa, f) => fa.pipe(mergeMap(f))} - is it really necessary to put it into the lib? What about other possible operator replacements? IMO switchMap covers 99% usecases.

Copy link
Collaborator

Choose a reason for hiding this comment

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

AFAIK exhaustMap waits for passed Observable to complete before subscribing to the new created one (result of chain). Looks like an incorrect behavior and I can't think of a use case for that.

Just a theorical question sure for the use cases we are looking at it does different things.

Replacing switchMap with mergeMap is a matter of const m = {...observable, chain: (fa, f) => fa.pipe(mergeMap(f))} - is it really necessary to put it into the lib? What about other possible operator replacements? IMO switchMap covers 99% usecases.

For the ReaderObservable this approach would not be practical and it would follow the implementation style of fp-ts like done for taskSeq. But of cause it could be part of another PR. Totally agree with the fact that switchMap covers most of the usecases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

follow the implementation style of fp-ts like done for taskSeq

Do you mean that there should be additional mergeReaderObservable and the same for any other instance using Observable as base?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thats what I would like to discuss to keep up with the fp-ts coding style. @gcanti any opinions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's tricky :) There's no way to test .toNotBe on TestScheduler's expectObservable, so we need to pass assert.notDeepStrictEqual as equality assertion function. Then we intentionally test that resulting observable does not equal marbles, because they are "incorrect" but should be the same as in success' RIGHT SIDE.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Waaaaaaaait... Let me recheck o_O

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, you are right, there's an error in the test. How could I miss it?... Anyway I updated the test and it still shows distributivity does not hold

Copy link
Owner

Choose a reason for hiding this comment

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

I have a proposal: for what I can undestand switching to switchMap is important to you, while a discussion about Alt / Alternative instances, albeit being very interesting from a theoretical point of view, is way less important in practice (it's just merge in the end), and currently is a blocker.

So what about:

  • replace mergeMap with switchMap (breaking change)
  • optimize filterMap implementation
  • remove Alt / Alternative instances from Observable, ObservableEither and ReaderObservable (it's a breaking change but we are already doing a breaking change for switchMap)
  • release v0.7
  • (maybe) continue the debate about Alternative in the future with less rush

@raveclassic @mlegenhausen what do you think?

Copy link
Contributor Author

@raveclassic raveclassic Apr 22, 2020

Choose a reason for hiding this comment

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

@gcanti We can keep zero as part of Plus typeclass, it doesn't require alt to be distributive. So that Observable is a Plus & Alt, but not an Alternative. This is not a breaking change, everything stays structurally equal.
But I'm open to any decision.

Copy link
Owner

@gcanti gcanti left a comment

Choose a reason for hiding this comment

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

@raveclassic I repeat my proposal, personally I don't like Plus precisely because is structurally equivalent to Alternative (no type checker help), that's why I removed Plus in v2

@raveclassic
Copy link
Contributor Author

Ok, got it, I'll update the PR.

@waynevanson
Copy link
Contributor

How did we go on this one?
Switch map is important.

Otherwise I may have to maintain a patched version of this repo if I am to use chain in projects.

If there is anything pending for gcanti's proposal of changes, what are they? I'd be happy to lend a hand.

@wmaurer
Copy link

wmaurer commented Apr 21, 2021

I know bumping github issues and PRs is bad form, but I also think switchMap really is important. chain is a core function, used by many other pipeable functions, and I'm finding it rather impractical to maintain my own versions so that I can have switchMap behaviour.

If nobody's willing to move this PR forward, should we create another simpler PR for the use of switchMap?

@wmaurer
Copy link

wmaurer commented Apr 21, 2021

OK, let's see what happens, new PR here:
#60

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Chain implementation causes memory leaks
5 participants