-
Notifications
You must be signed in to change notification settings - Fork 158
Should flatMap be map? #15
Comments
Which of the 3 implementations of
|
Correct me if I am wrong, but I learned the idea of flatMap from Haskell, which is
Because If you want to add error handling to signal, it should be a The current implementation of Signal is actually 2 signals combined, one for |
Yes, you're right: These are not signals from haskell, they're working a bit differently (e.g. they have internal state). Signal is just an async wrapper that makes it possible to execute a chain of If |
I think @b123400 is making the same general argument as Gordon Fontenot makes here: http://buildphase.fm/90 (regarding Basically that I think you can definitely decide to use other meanings of |
I completely get your point. The only problem in renaming I see is that it's not map either: the current version gets the T of the Signal and returns a result of T, but the resulting Signal is of type T, not Result (Although you could argue that the signal holds a result internally of course). Currently the implementations of signal and result are so much intertwined that it's not possible to use the signal without result. I'm thinking about a version 2 which splits them up - but I don't see that much value in a signal without error handling. |
So in total: it's not map (wrong argument of the transform) and not flatMap (wrong result type). But flatMap gets closer to the intention of flattening the results. |
Yeah, that’s the tricky part. Though I think a consumer of the API shouldn’t be quite so concerned with the technical implementation details regarding Back to the specific // This is definitely `map`
func map<U>(f: T -> U) -> Signal<U>
// I can see the argument for calling this `flatMap`,
// similar to Array<T>’s flatMap(T -> Optional<U>) -> Array<U>
// ReactiveCocoa calls this `attemptMap`
func flatMap<U>(f: T -> Result<U>) -> Signal<U>
// This feels like like `map` to me. I know under the hood it’s
// essentially the same as the previous, but it’s signature is
// basically just `T -> U`. In fact, with the current implementation,
// you can pass the same (non-throwing) `T -> U` function to both
// `map` and this `flatMap`, which definitely feels weird.
//
// I vote remove this one and make `map` take `T throws -> U`, which
// lets you use it with either a throwing or non-throwing function.
// (Unless you want to introduce `attemptMap` in which case I *could*
// see keeping this as a version of that to keep it explicitly visible where
// errors can be introduced into a signal, which I actually like better
// personally).
func flatMap<U>(f: T throws -> U) -> Signal<U>
// I have no idea what this one is. It’s shape is more like a subscription/sink
// than an operation.
func flatMap<U>(f: (T, (Result<U>->Void))->Void) -> Signal<U> Conspicuously missing is a true |
(Also, I’d be remiss not to point out that I’m by no means an authority on any of this. I’m only just starting to get a feel for these particular functional programming conventions. So please take my comments as simply sharing some ideas and observations, not strong/strict criticism. 😄) |
Thanks for the replies,, how about rename it to something like |
I like the idea of introducing I also thought about naming it In the end this should be created as an extension to signals containing a result, as this operation is only permitted on result-signals and there is no usecase of having a then instead of a map for value-based signals. |
Sounds awesome – being able to build more |
|
flatMap should have a function signature of
Signal a -> (a -> Signal b) -> Signal b
, but it is currentlySignal a -> (a -> b) -> Signal b
which is a functor mapping, not flatMapping.The text was updated successfully, but these errors were encountered: