Skip to content

Future#432

Open
tshemsedinov wants to merge 3 commits intomasterfrom
future
Open

Future#432
tshemsedinov wants to merge 3 commits intomasterfrom
future

Conversation

@tshemsedinov
Copy link
Copy Markdown
Member

@tshemsedinov tshemsedinov commented Jun 9, 2019

Closes: #431

Comment thread lib/future.js Outdated
Comment thread lib/future.js
@tshemsedinov
Copy link
Copy Markdown
Member Author

ping @nechaido

Comment thread lib/future.js
}
})
);
}
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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 :-)

Comment thread lib/future.js

promise() {
return new Promise((resolve, reject) => {
this.run(value => resolve(value), error => reject(error));
Copy link
Copy Markdown
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
Copy Markdown
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

Comment thread lib/future.js

chain(fn) {
return new Future((resolve, reject) =>
this.run(value => fn(value).run(resolve, reject), error => reject(error))
Copy link
Copy Markdown
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
Copy Markdown
Member

Choose a reason for hiding this comment

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

ditto

Comment thread test/future.js
const { Future, futurify } = require('..');
const metatests = require('metatests');

metatests.test('Future map/run', async test => {
Copy link
Copy Markdown
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)

Comment thread test/future.js

metatests.test('Future stateless', async test => {
const f1 = Future.of(3);
const f2 = f1.map(x => ++x);
Copy link
Copy Markdown
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);

Comment thread test/future.js
.map(() => {
throw new Error('msg');
})
.run(test.mustNotCall, error => {
Copy link
Copy Markdown
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 => {

Comment thread lib/future.js
}
})
);
}
Copy link
Copy Markdown
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.

Comment thread lib/future.js
}

run(successed, failed) {
this.executor(successed, failed);
Copy link
Copy Markdown
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

Comment thread lib/future.js
}
})
);
}
Copy link
Copy Markdown
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