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

Future #432

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

Future #432

wants to merge 3 commits into from

Conversation

tshemsedinov
Copy link
Member

@tshemsedinov tshemsedinov commented Jun 9, 2019

Closes: #431

lib/future.js Outdated Show resolved Hide resolved
lib/future.js Show resolved Hide resolved
@tshemsedinov
Copy link
Member Author

ping @nechaido

}
})
);
}
Copy link
Member

Choose a reason for hiding this comment

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

Could this perhaps be rewritten without double Future?

  map(fn) {
    return new Future((resolve, reject) => {
      this.run(value => {
        try {
          resolve(fn(value));
        } catch (error) {
          reject(error);
        }
      });
    });

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for bringing the attention here!
I was able to spot the inconsistency of error handling.
The ideologically correct way to implement map function for any monad is the application of chain to the composition of of and fn. Rambda did it.
To do it here one simply needs to move error checking from here to run method.

Copy link
Member

Choose a reason for hiding this comment

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

Implementation of map through already existed chain and of is less efficient because of the creation of two Futures. That said such implementation reuses code and is the same for any monad. Though the duplicated code here is tiny meaning it may be allowed here for clarity.
So there is a tradeoff between performance and ideological purity here :-)


promise() {
return new Promise((resolve, reject) => {
this.run(value => resolve(value), error => reject(error));
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
this.run(value => resolve(value), error => reject(error));
this.run(value => resolve(value), reject);

Copy link
Member

Choose a reason for hiding this comment

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

I think the lambda is used for dropping other arguments


chain(fn) {
return new Future((resolve, reject) =>
this.run(value => fn(value).run(resolve, reject), error => reject(error))
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
this.run(value => fn(value).run(resolve, reject), error => reject(error))
this.run(value => fn(value).run(resolve, reject), reject)

Copy link
Member

Choose a reason for hiding this comment

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

ditto

const { Future, futurify } = require('..');
const metatests = require('metatests');

metatests.test('Future map/run', async test => {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
metatests.test('Future map/run', async test => {
metatests.test('Future map/run', test => {

?

(and in other tests)


metatests.test('Future stateless', async test => {
const f1 = Future.of(3);
const f2 = f1.map(x => ++x);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const f2 = f1.map(x => ++x);
const f2 = f1.map(x => x + 1);

.map(() => {
throw new Error('msg');
})
.run(test.mustNotCall, error => {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.run(test.mustNotCall, error => {
.run(test.mustNotCall(), error => {

}
})
);
}
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for bringing the attention here!
I was able to spot the inconsistency of error handling.
The ideologically correct way to implement map function for any monad is the application of chain to the composition of of and fn. Rambda did it.
To do it here one simply needs to move error checking from here to run method.

}

run(successed, failed) {
this.executor(successed, failed);
Copy link
Member

Choose a reason for hiding this comment

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

I think it's important to catch errors the executor may throw. It will also allow omitting error handling in the map method

}
})
);
}
Copy link
Member

Choose a reason for hiding this comment

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

Implementation of map through already existed chain and of is less efficient because of the creation of two Futures. That said such implementation reuses code and is the same for any monad. Though the duplicated code here is tiny meaning it may be allowed here for clarity.
So there is a tradeoff between performance and ideological purity here :-)

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

Successfully merging this pull request may close these issues.

Add Future implementation
3 participants