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

Why do you prefer flattenMap: over map:? #448

Closed
shpakovski opened this Issue Apr 24, 2013 · 13 comments

Comments

Projects
None yet
@shpakovski
Contributor

shpakovski commented Apr 24, 2013

Hi guys,

There is a famous example from the RAC announcement blog post:

[[[[client loginUser] flattenMap:^(User *user) {
    return [client loadCachedMessagesForUser:user];
}] flattenMap:^(NSArray *messages) {
    return [client fetchMessagesAfterMessage:messages.lastObject];
}] subscribeCompleted:^{
    NSLog(@"Fetched all messages.");
}];

It looks like the same effect would be from code like this:

[[[[client loginUser] map:^(User *user) {
    return [client loadCachedMessagesForUser:user];
}] map:^(NSArray *messages) {
    return [client fetchMessagesAfterMessage:messages.lastObject];
}] subscribeCompleted:^{
    NSLog(@"Fetched all messages.");
}];

Could you please explain why do you prefer flattenMap: over map:? Maybe there is some less visible pitfall? Is it correct at all to use map: for such cases?

Thanks in advance!

@shpakovski shpakovski closed this Apr 24, 2013

@shpakovski shpakovski reopened this Apr 24, 2013

@kastiglione

This comment has been minimized.

Show comment
Hide comment
@kastiglione

kastiglione Apr 24, 2013

Member

The block passed to -flattenMap: must return a signal. For -map:, the return value from the supplied block is wrapped in a signal using -[RACStream return:].

Your second example with -map:, would create a signal of signals. Which you may or may not want.

Member

kastiglione commented Apr 24, 2013

The block passed to -flattenMap: must return a signal. For -map:, the return value from the supplied block is wrapped in a signal using -[RACStream return:].

Your second example with -map:, would create a signal of signals. Which you may or may not want.

@shpakovski

This comment has been minimized.

Show comment
Hide comment
@shpakovski

shpakovski Apr 24, 2013

Contributor

Got it, thank you very much.

Contributor

shpakovski commented Apr 24, 2013

Got it, thank you very much.

@shpakovski shpakovski closed this Apr 24, 2013

@hfossli

This comment has been minimized.

Show comment
Hide comment
@hfossli

hfossli Feb 13, 2014

So in general. Use -map: whenever you want to map a value to a value. And use -flattenMap: whenever you want to map a value to another signal. Right?

hfossli commented Feb 13, 2014

So in general. Use -map: whenever you want to map a value to a value. And use -flattenMap: whenever you want to map a value to another signal. Right?

@notxcain

This comment has been minimized.

Show comment
Hide comment
@notxcain

notxcain Feb 13, 2014

@hfossli the second case depends on what behavior you espect. Trasnformation Value -> Signal could be done via -map:] switchToLatest]. But the logic differs.

notxcain commented Feb 13, 2014

@hfossli the second case depends on what behavior you espect. Trasnformation Value -> Signal could be done via -map:] switchToLatest]. But the logic differs.

@erikprice

This comment has been minimized.

Show comment
Hide comment
@erikprice

erikprice Feb 13, 2014

Member

Use -map: whenever you want to map from a value to another value. There is no difference between a "value" and a "signal" in this context. Values are just objects that can be sent on a signal, and signals can send signals as their values. They aren't treated any different from other objects in the context of -map:.

In other words, any of the following mappings of input values to output values are valid:

input value output value
non-signal non-signal
non-signal signal
signal non-signal
signal signal

On the other hand, -flattenMap: is used whenever you want to map from an input value to an output value that is a signal, and you want that output signal to be flattened into the receiver's stream. The input value being mapped could be a signal, but it doesn't have to be. But the output value returned from the block must be a signal, and furthermore, that signal's values will be -flattened into the receiver's stream.

input value output value
non-signal signal
signal signal

This "flattening" means that the subscriber to the signal that is being -flattenMap:-ed will receive the values sent by the signal returned from -flattenMap: as if it had subscribed to that signal (because that is exactly what -flattenMap: does).

The difference is that if you were to -map: an input value (regardless of whether it is a signal or not) to an output value that is a signal, the subscriber is not automatically subscribed to that output signal. It just receives the signal as it would any other value.

Two related operations that might be worth learning more about are -flatten (not to be confused with -flatten:) and -concat. If you understand -map: and -flatten, then you can understand -flattenMap:.

Member

erikprice commented Feb 13, 2014

Use -map: whenever you want to map from a value to another value. There is no difference between a "value" and a "signal" in this context. Values are just objects that can be sent on a signal, and signals can send signals as their values. They aren't treated any different from other objects in the context of -map:.

In other words, any of the following mappings of input values to output values are valid:

input value output value
non-signal non-signal
non-signal signal
signal non-signal
signal signal

On the other hand, -flattenMap: is used whenever you want to map from an input value to an output value that is a signal, and you want that output signal to be flattened into the receiver's stream. The input value being mapped could be a signal, but it doesn't have to be. But the output value returned from the block must be a signal, and furthermore, that signal's values will be -flattened into the receiver's stream.

input value output value
non-signal signal
signal signal

This "flattening" means that the subscriber to the signal that is being -flattenMap:-ed will receive the values sent by the signal returned from -flattenMap: as if it had subscribed to that signal (because that is exactly what -flattenMap: does).

The difference is that if you were to -map: an input value (regardless of whether it is a signal or not) to an output value that is a signal, the subscriber is not automatically subscribed to that output signal. It just receives the signal as it would any other value.

Two related operations that might be worth learning more about are -flatten (not to be confused with -flatten:) and -concat. If you understand -map: and -flatten, then you can understand -flattenMap:.

@hfossli

This comment has been minimized.

Show comment
Hide comment
@hfossli

hfossli Feb 13, 2014

Great read. Thanks. This makes sense!

hfossli commented Feb 13, 2014

Great read. Thanks. This makes sense!

@RuiAAPeres

This comment has been minimized.

Show comment
Hide comment
@RuiAAPeres

RuiAAPeres Mar 13, 2014

Member

@jspahrsummers & @joshaber I have to say that @erikprice answer is spot on, and could well be used as a helper link here. It sure did help me understanding better what's happening with -map: -flattenMap:.

Member

RuiAAPeres commented Mar 13, 2014

@jspahrsummers & @joshaber I have to say that @erikprice answer is spot on, and could well be used as a helper link here. It sure did help me understanding better what's happening with -map: -flattenMap:.

@jspahrsummers

This comment has been minimized.

Show comment
Hide comment
@jspahrsummers

jspahrsummers Mar 13, 2014

Member

Pull requests welcome! 😉

Member

jspahrsummers commented Mar 13, 2014

Pull requests welcome! 😉

@AndresCianio

This comment has been minimized.

Show comment
Hide comment
@AndresCianio

AndresCianio Apr 10, 2014

Wow... Thanks @erikprice for this explanation... It actually blew my mind once I saw it so clearly. Thanks!

AndresCianio commented Apr 10, 2014

Wow... Thanks @erikprice for this explanation... It actually blew my mind once I saw it so clearly. Thanks!

@onmyway133

This comment has been minimized.

Show comment
Hide comment
@onmyway133

onmyway133 Mar 28, 2015

A very interesting explanation is in this article http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1

[[[self.signInButton
   rac_signalForControlEvents:UIControlEventTouchUpInside]
   flattenMap:^id(id x) {
     return [self signInSignal];
   }]
   subscribeNext:^(id x) {
     NSLog(@"Sign in result: %@", x);
   }];

This maps the button touch event to a sign-in signal as before, but also flattens it by sending the events from the inner signal to the outer signal.

And a very good post on map vs flattenmap Functor and Monad in Swift

onmyway133 commented Mar 28, 2015

A very interesting explanation is in this article http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1

[[[self.signInButton
   rac_signalForControlEvents:UIControlEventTouchUpInside]
   flattenMap:^id(id x) {
     return [self signInSignal];
   }]
   subscribeNext:^(id x) {
     NSLog(@"Sign in result: %@", x);
   }];

This maps the button touch event to a sign-in signal as before, but also flattens it by sending the events from the inner signal to the outer signal.

And a very good post on map vs flattenmap Functor and Monad in Swift

@hsavit1

This comment has been minimized.

Show comment
Hide comment
@hsavit1

hsavit1 Jun 28, 2015

Very cool @onmyway133 . I've never used flattenMap like that. Thanks for sharing

hsavit1 commented Jun 28, 2015

Very cool @onmyway133 . I've never used flattenMap like that. Thanks for sharing

@alper

This comment has been minimized.

Show comment
Hide comment
@alper

alper Dec 28, 2016

Contributor

What do you mean with: map from an input value to an output value that is a signal, and you want that output signal to be flattened into the receiver's stream.

Contributor

alper commented Dec 28, 2016

What do you mean with: map from an input value to an output value that is a signal, and you want that output signal to be flattened into the receiver's stream.

@andersio

This comment has been minimized.

Show comment
Hide comment
@andersio

andersio Dec 28, 2016

Member

map maps discrete time value one-to-one.

flatMap is essentially map-then-flatten — It first maps a value to a signal of U values. This means the resulting signal would become a signal of signals of U values. Then it would flatten the signal of signals of U values into just a signal of U values, based on the strategy you choose (concat, merge or switchToLatest).

Member

andersio commented Dec 28, 2016

map maps discrete time value one-to-one.

flatMap is essentially map-then-flatten — It first maps a value to a signal of U values. This means the resulting signal would become a signal of signals of U values. Then it would flatten the signal of signals of U values into just a signal of U values, based on the strategy you choose (concat, merge or switchToLatest).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment