-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
flatMap
expectations
#773
Comments
I'm copying Optional<Data>.none.flatMap(UIImage.init) // => nil
Data().flatMap(UIImage.init) // => nil
validImageData.flatMap(UIImage.init) // => UIImage
Optional<Int>.none.map{ $0 + 1 } // => nil
Optional.some(1).map{ $0 + 1 } // => 2 |
I didn't have time right this second to read your post thoroughly, but I will. Just thought I'd throw the above in in case it helped. |
Ah, so Optional's Optional<A> => ((A) -> Optional<B>) -> Optional<B> So to define Promise<A> => ((A) -> Promise<B>) -> Promise<B> Array works the same way. ( Compare with [A?] => ((A?) -> B?) -> [B] These overloads on Promise are definitely handy! They're just not quite what folks expect when they see Hopefully this all makes sense! We're in abstract land, so it can be tough to convey this stuff. I'm hoping seeing the function signatures helps! |
OK, what do you suggest? |
Just some renaming. Earlier I suggested the idea of sharing What do you think? |
For me, the way to think about promises is to imagine they didn't exist, to abstract away the asynchronicity. Thus it's like you called Of course this falls flat since the value is not an optional mostly. So really we should call it Anyway, I'm all for not confusing functional names more in any land, especially Swift land. So provided we can figure out good names, I'm down. In this respect, is |
I don't think so! From what I gather Promise<T> => ((T) -> U) -> Promise<U> PromiseKit/Sources/Thenable.swift Line 38 in 0e150f8
This is exactly what we want when we encounter |
I hear ya! I just think this is a tough goal to achieve and it's what |
One other thought before I call it a night: there are some nice methods on observable/signal types in FRP libraries like ReactiveSwift and Rx that |
Just brainstorming:
Or should we stick clear of |
tl;dr I think the feeling of the way PMK uses these functions is spot on, which IMO is at least as important as using them accurately. I don't feel qualified to comment on the technical underpinnings of @stephencelis's reasoning here, which I'm sure is sound. I don't have any experience with functional programming, and my understanding of these transforming functions has come to me more through feeling than concrete expectations. I imagine I'm not the only PMK user in this boat though. So I'm gonna throw in my two cents, which admittedly is mostly opinion. I understood the purpose of these new functions almost immediately. I did need to read the documentation on them first. What did it mean to These understandings were based on the feeling of my experience with the functions they're based on, not the technical expectations, which I've never had to think about. A lot of people do think about these things of course, and I can understand how our co-opting of the functions might be confusing due to that. So if some better names are found, then cool. But I do worry about adding qualifiers to the names (e.g. |
@nathanhosselton This is exactly why The current implementations in PromiseKit unfortunately deviate from the long history of what I think the constructive way to continue the conversation here is to suggest new names that feel right and don't overload concepts that have well-established meaning. |
Apologies, restating information here so I am sure I'm on the same page. The issue with our The issue with our Is this right? |
@nathanhosselton I'm not exactly clear as to what you're asking, but none of the (M<A>) => ((A) -> M<B>) -> M<B> It operates on a single container type, I think it's important to realize that |
I'm just trying to restate the specific issue(s) with our flatMaps as succinctly as possible. But I want to confirm with you that I have it right, since this is my first time considering such things. As you said, the shape of a pure flatMap looks like: (M<A>) => ((A) -> M<B>) -> M<B> And the shape of our flatMaps actually look like: //Optional Thenable.flatMap
(Promise<A>) => ((A) -> Optional<B>) -> Promise<B>
//or
(M<A>) => ((A) -> N<B>) -> M<B>
//Sequence Thenable.flatMap
(Promise<Sequence<A>>) => ((A) -> Sequence<B>) -> Promise<Sequence<B>>
//or
(M<A>) => ((A) -> B) -> M<B> In the case of our flatMap on Sequences, the shape becomes correct if the promise containers are ignored, which is why you suggested the name In the case of our flatMap on Optionals, the shape is still incorrect even when the promise containers are ignored: (A) => ((A) -> N<B>) -> B …which is why @mxcl mentioned that the name Do I have this right? |
I think you have this right!
In this case, no need to abstract with Array<A> => ((A) -> Optional<B>) -> Array<B>
Promise<A> => ((A) -> Optional<B>) -> Promise<B> If we settled on this, we'd have to rename the existing overload from |
What about
|
Either sound good to me! |
Yeah, so really K, well I don't want to muddy anything here, let’s rename both Mainly my concern is a sensible set of names that people understand without having to read the documentation. Then after that is that we don’t contribute to a wider misunderstanding of functional principles, and since PromiseKit is a popular library, we have that responsibility. Thanks for the input, let's keep it going. |
firstly {
fetchImageData()
}.ifLet {
UIImage(data: $0)
} Clear, but ugly. Probably should be our choice, but boy… |
firstly {
fetchImageData()
}.unwrap {
UIImage(data: $0)
} |
After reading your conversation I feel that |
So we have:
How are we feeling about these? Additionally, how about |
Could |
I think Array<A> => ((A) -> Optional<B>) -> Array<B>
Promise<A> => ((A) -> Optional<B>) -> Promise<B> Then Promise<Array<A>> => ((A) -> Optional<B>) -> Promise<Array<B>>
Hm, interesting. So if I came across func filter<A>(_ predicate: (A) -> Bool) -> Promise<A> {
return self.compactMap { predicate($0) ? $0 : nil }
} |
Please review this preliminary PR: #782 |
Rename functional functions; Refs #773 See the discussion in #773 and #782 for details. | Form | Currently | Proposed | |-------------------------------------------|---------|------------| | `Promise<T> => ((T) -> U?) => Promise<U>` | flatMap | compactMap | | `Promise<[T]> => ((T) -> U) => Promise<[U]>` | map | mapValues | | `Promise<[T]> => ((T) -> [U]) => Promise<[U]>)` | flatMap | flatMapValues | | `Promise<[T]> => ((T) => U?)` | compactMap | compactMapValues | | `Promise<[T]> => ((T) -> Promise<U>) => Promise<[U]>` | thenMap | *unchanged* | | `Promise<[T]> => ((T) -> Promise<[U]>) => Promise<[U]>` | thenFlatMap | *unchanged* | | `Promise<[T]> => ((T) -> Bool) => Promise<[T]>` | filter | filterValues | | `Promise<[T]> => Promise<T>` | last | lastValue |
Merged for release with 6.1.0 (today hopefully). |
I know we're in maintenance mode here, but I was wondering if you'd be open to a small discussion on the
flatMap
introductions in the most recent version, since Swift's long overloaded the term and made it more confusing than it needs to be (and again, recently, withcompactMap
), and I'm afraid the naming here may continue to bewilder folks both new to and familiar with functional programming.The concepts of
map
andflatMap
in programming at large have this shape:For PromiseKit to behave as one familiar these shapes expects,
map
andflatMap
should look like this:These are the two behaviors of
then
! The Promises/A+ spec decided to combinemap
andflatMap
behaviors for ergonomics. Overloads in JavaScript don't have the compiler error issues you've been trying to solve, but having predictablemap
andflatMap
would help!The
flatMap
additions to PromiseKit run counter to these intuitions. Maybe some qualifiers would help.PromiseKit/Sources/Thenable.swift
Line 57 in 0e150f8
This looks like it's a
compactMap
of sorts, swallowing up optional return values.PromiseKit/Sources/Thenable.swift
Line 176 in 0e150f8
This is performing a
flatMap
operation on the inner sequence, not the promise, so maybeflatMapInner
would be a more appropriate name.Lemme know if I've been unclear or need to make any clarifications!
The text was updated successfully, but these errors were encountered: