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

update bimap implementation to reject result of leftFn, and improve type signature #1

Merged
merged 1 commit into from
Mar 28, 2019

Conversation

interacsean
Copy link
Contributor

Updated the return type to be Promise, as it doesn't have to be the same type as the original promise.

I updated the test as it didn't look quite right – I think this method (.then, .catch rather than try/catch block) feels a bit more robust. I will update other tests to use this as well (and have similar updates for type signatures) if you're happy with it.

Some additional updates happened to index files as a result of running build

@dandro dandro self-assigned this Mar 10, 2019
Copy link
Owner

@dandro dandro left a comment

Choose a reason for hiding this comment

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

This is awesome man! thanks for that, specially the fact that bimap now maintains the state of the promise (as in rejected will remain rejected). As for changing from try/catch to then/catch it technically doesn't make any difference, so I'm fine if you want to change it but it's probably not worth much effort TBH.

): Promise<T> {
return p.then(rightFn, leftFn);
): Promise<R> {
return p.then(rightFn, (e: E): Promise<R> => Promise.reject(leftFn(e)));
Copy link
Owner

Choose a reason for hiding this comment

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

Hey man, the type here is wrong... it should be (e: E): Promise<any> => ... since the leftFn is (E) => any. Which I should fix to be honest because we should not be using weak types.

Copy link
Contributor Author

@interacsean interacsean Mar 10, 2019

Choose a reason for hiding this comment

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

I wasn't sure what to do here. I still half-feel this is correct.

If we were to write out the rightFn it would look like this

function bimap<T, R, E>(
  leftFn: (E) => any,
  rightFn: (T) => R,
  p: Promise<T>
): Promise<R> {
  return p.then(
    **(v: T): Promise<R> => Promise.resolve(rightFn(v)),**
    (e: E): Promise<R> => Promise.reject(leftFn(e)),
  );
}

Since rightFn returns Promise<R>, shouldn't leftFn return Promise<R> too – so that the function on both sides returns the same type?

(If leftFn returned Promise<any>, would bimap then return Promise<any>?)

In reality, once the promise is failed, it won't contain an R anymore, and of course we can't annotate the type of the rejected state value

Copy link
Owner

Choose a reason for hiding this comment

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

Oh I see the confusion. So promises only annotate the Success value, not the error. So as long as the promise on the left is rejected we cannot type it.
facebook/flow#1232
facebook/flow#5042

Alternatively we could say, incorrectly, Promise<R | SomeErrorType> but that's just us being super clever :)

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 that's my understanding. Which is a huge caveat of this approach, but I still think there's a lot of benefits to it which is why I'm PRing :).

Unfortunately that union trick won't work for long:

https://flow.org/try/#0PTAEAEDMBsHsHcBQiAuBPADgU1AVQM5YBOoAvKAN6gB2AhgLZYBco+KRAltQOagC+AblSYcAUSJFYJclQDGsACbMaAV3oAjYvyGJIK6rJQdY1UNywoCxAEJoAkgoAUHBQEoWABUn0OhADxWJAA+oOKSRAB8lKBEFipEpl6wPoQAdLH4sNAAbliOVHSMLADkAMpYtNTF-K4C-Mjy1GygKoREnt6+WAFtoCFhUlHk5pZttg6OAIy1yK3EqSgAFljUjo5LWIEsga5kURSIoKCNmdBYqXDc68uBqYVYM3yuqbK0KLKLa8TtoRJSu6R9odjiZTudLo5ivIVNAFDRYCgzBYWm1qgBqUDfF6KB5CJ5AA

As a comment to the issues around being unable to type the rejection because anything might be thrown, as long as every Promise has a catch((err: any) => ConcreteErrVal) defined, shouldn't it then be possible to type the resulting Promises' rejected side with that Promise<SuccessType, ConcreteErrVal>. (And prior to that catch, it would have to be Promise<SuccessVal, any>?)

Getting off topic... how should we resolve the bimap?

@@ -3,27 +3,37 @@
import bimap from './bimap';

describe('async/bimap', () => {
test('should only call left fn if promise rejects', async (): Promise<any> => {
const leftFn: Function = jest.fn();
test('should only call leftFn if promise rejects', async (done: Function): Promise<any> => {
Copy link
Owner

Choose a reason for hiding this comment

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

If you're going to change from try/catch to .then/catch the can you remove the async keywords?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do

@dandro
Copy link
Owner

dandro commented Mar 28, 2019

Sorry dude, I'll merge it now as it makes sense enough and I honestly don't know the answer, but I think it makes sense (until we can type Errors in Promises).

@dandro dandro merged commit 949a55c into dandro:master Mar 28, 2019
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.

2 participants