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

Rename Repromise.then_ to Repromise.andThen #27

Merged
merged 3 commits into from
Jul 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions doc/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ let (p, resolve_p) = Repromise.make();
resolve_p("Hello");
```

`p` is resolved with `"Hello"`. Any callbacks that have been attached to `p` by [`wait`](#wait), [`map`](#map), [`then_`](#then), [`all`](#all), or [`race`](#race), get called, and receive `"Hello"` as their argument. See examples in the rest of the documentation below.
`p` is resolved with `"Hello"`. Any callbacks that have been attached to `p` by [`wait`](#wait), [`map`](#map), [`andThen`](#andThen), [`all`](#all), or [`race`](#race), get called, and receive `"Hello"` as their argument. See examples in the rest of the documentation below.

Each promise can only be resolved once, so calling `resolve_p` again has no effect.

Expand Down Expand Up @@ -128,10 +128,10 @@ resolve_p("Hello");

<br/>

## `then_`
## `andThen`

```reason
then_: (('a => Repromise.t('b)), Repromise.t('a)) => Repromise.t('b)
andThen: (('a => Repromise.t('b)), Repromise.t('a)) => Repromise.t('b)
```

Like [`map`](#map), but the return value of the callback is another promise. This allows the callback to start an async operation:
Expand All @@ -141,7 +141,7 @@ let (p1, resolve_p1) = Repromise.make();
Js.Global.setTimeout(() => resolve_p1(), 1000) |> ignore;

p1
|> Repromise.then_(() => {
|> Repromise.andThen(() => {
print_endline("Hello");

let (p2, resolve_p2) = Repromise.make();
Expand Down
14 changes: 4 additions & 10 deletions doc/docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ Repromise also has to provide [its own versions](API#then) of [`then`][Promise.t

<br/>

## Why does JS have only `then`, but Repromise has `then_`, `map`, and `wait`?
## Why does JS have only `then`, but Repromise has `andThen`, `map`, and `wait`?

In JavaScript, [`Promise.then`][Promise.then] behaves differently depending on the (dynamic) type of the value returned by its callback. These different behaviors can't be assigned one type, so `then` is split into `then_`, `map`, and `wait`.
In JavaScript, [`Promise.then`][Promise.then] behaves differently depending on the (dynamic) type of the value returned by its callback. These different behaviors can't be assigned one type, so `then` is split into `andThen`, `map`, and `wait`.

1. The most general behavior occurs when the callback returns another promise. This allows chaining asynchronous operations:

Expand All @@ -90,7 +90,7 @@ In JavaScript, [`Promise.then`][Promise.then] behaves differently depending on t

The first `.then` is the important one. Its callback returns a new promise, and the chain of asynchronous operations continues by waiting for that promise.

This corresponds to [`Repromise.then_`](API#then), whose callback returns a promise.
This corresponds to [`Repromise.andThen`](API#andThen), whose callback returns a promise.

2. JS promises support another behavior, which occurs when the callback of `.then` returns a non-promise value. In this case, the value is directly passed to the next `.then` in the chain:

Expand All @@ -106,7 +106,7 @@ In JavaScript, [`Promise.then`][Promise.then] behaves differently depending on t

Again, the first `.then` is the important one. Since its callback does not return a promise, the chain doesn't have to wait. It continues (almost) immediately into the second `.then`, which prints `43`.

[`Repromise.then_`](API#then) can't be used for this `.then`, because the callback of [`Repromise.then_`](API#then) must return a promise. Reason's type system won't allow returning a non-promise value.
[`Repromise.andThen`](API#andThen) can't be used for this `.then`, because the callback of [`Repromise.andThen`](API#andThen) must return a promise. Reason's type system won't allow returning a non-promise value.

So, for this case, Repromise provides [`Repromise.map`](API#map), whose callback can return a non-promise value.

Expand Down Expand Up @@ -135,12 +135,6 @@ See [this post](https://github.com/aantron/repromise/issues/22#issuecomment-4055

<br/>

## Why does `then_` have an underscore?

`then` is not a keyword in Reason, but Reason is supposed to still parse when transformed to OCaml, and `then` is a keyword in OCaml.

<br/>

## Why is there Repromise, when there are already Lwt and Async?

[Lwt](https://github.com/ocsigen/lwt) and [Async](https://github.com/janestreet/async) are two promise libraries from OCaml's native ecosystem.
Expand Down
4 changes: 2 additions & 2 deletions doc/docs/interop.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Repromise.resolve(Repromise.resolve(1)) |> Js.log;
/* Promise { WrappedRepromise { wrapped: Promise { 1 } } } */
```

Repromise functions (such as [`wait`](API#wait) and [`then_`](API#then)) silently insert and remove this wrapper object, as needed.
Repromise functions (such as [`wait`](API#wait) and [`andThen`](API#andThen)) silently insert and remove this wrapper object, as needed.

### Takeaway

Expand Down Expand Up @@ -62,7 +62,7 @@ let url =

let () =
fetch(url)
|> Repromise.then_(text)
|> Repromise.andThen(text)
|> Repromise.wait(Js.log);
```

Expand Down
2 changes: 1 addition & 1 deletion doc/docs/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ So,

- When given a nested promise, [`Repromise.resolve`](API#resolve) is slower than `Js.Promise.resolve`. However, this is because `Js.Promise.resolve` is [not type-safe](DesignFAQ#why-are-js-promises-not-type-safe), and simply returns its argument as its result. `Repromise.resolve`, instead, allocates two new objects: a new JS promise, and a [wrapper object](Interop#representation) to prevent the outer and inner promises from collapsing into each other. These two allocations are probably the reason why `Repromise.resolve` is up to about twice as slow on nested promises, as on non-promise values.

- [`Repromise.then_`](API#then) is about half as fast as `Js.Promise.then`. This is due to dynamic checks for nested promises, and due to [setting up asynchronous exception handling](API#onunhandledexception). However, the difference is not significant. `then_` is almost always used around I/O, which takes much longer than tens of nanoseconds to complete.
- [`Repromise.andThen`](API#then) is about half as fast as `Js.Promise.then`. This is due to dynamic checks for nested promises, and due to [setting up asynchronous exception handling](API#onunhandledexception). However, the difference is not significant. `andThen` is almost always used around I/O, which takes much longer than tens of nanoseconds to complete.
6 changes: 3 additions & 3 deletions doc/docs/rejectable.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,15 +177,15 @@ Like the normal [`Repromise.map`](API#map), but works on rejectable Repromises.

<br/>

## `then_`
## `andThen`

```reason
then_:
andThen:
(('a => Repromise.Rejectable.t('b, 'e)), Repromise.Rejectable.t('a, 'e)) =>
Repromise.Rejectable.t('b, 'e)
```

Like the normal [`Repromise.then_`](API#then), but works on rejectable Repromises. If the Repromise gets rejected (instead of resolved), the callback is not called, and the error is propagated to the final promise returned by `then_`.
Like the normal [`Repromise.andThen`](API#then), but works on rejectable Repromises. If the Repromise gets rejected (instead of resolved), the callback is not called, and the error is propagated to the final promise returned by `andThen`.

<br/>

Expand Down
6 changes: 3 additions & 3 deletions src/js/repromise.re
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ module Rejectable = {
external resolve: 'a => rejectable('a, _) = "";

[@bs.val]
external then_:
external andThen:
('a => rejectable('b, 'e), rejectable('a, 'e)) => rejectable('b, 'e) =
"then";

let map = (callback, promise) =>
promise |> then_(value => resolve(callback(value)));
promise |> andThen(value => resolve(callback(value)));

let wait = (callback, promise) =>
promise |> map(callback) |> ignore;
Expand Down Expand Up @@ -157,7 +157,7 @@ let make = () => {
};

let resolve = Rejectable.resolve;
let then_ = Rejectable.then_;
let andThen = Rejectable.andThen;
let map = Rejectable.map;
let wait = Rejectable.wait;
let all = Rejectable.all;
Expand Down
4 changes: 2 additions & 2 deletions src/js/repromise.rei
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ let make: unit => (promise('a), 'a => unit);

let resolve: 'a => promise('a);

let then_: ('a => promise('b), promise('a)) => promise('b);
let andThen: ('a => promise('b), promise('a)) => promise('b);

let map: ('a => 'b, promise('a)) => promise('b);

Expand All @@ -33,7 +33,7 @@ module Rejectable: {

let reject: 'e => rejectable(_, 'e);

let then_:
let andThen:
('a => rejectable('b, 'e), rejectable('a, 'e)) => rejectable('b, 'e);

let map: ('a => 'b, rejectable('a, 'e)) => rejectable('b, 'e);
Expand Down
10 changes: 5 additions & 5 deletions src/native/repromise.re
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type t('a) = promise('a);


/* The `Merged constructor and this function, underlying, are used to avoid a
memory leak that arises when then_ is called on promises in a loop. See the
memory leak that arises when andThen is called on promises in a loop. See the
description in the associated test "promise loop memory leak". The rest of
this comment is based on that description.

Expand Down Expand Up @@ -152,7 +152,7 @@ let makePromiseBehaveAs = (outerPromise, nestedPromise) => {
| `Fulfilled(_)
| `Rejected(_) =>
/* These two cases are impossible, because if makePromiseBehaveAs is
called, then_ or catch_ called the callback that was passed to it, so
called, andThen or catch_ called the callback that was passed to it, so
the outer promise is still pending. It is this function which resolves
the outer promise. */
assert(false);
Expand All @@ -173,7 +173,7 @@ let makePromiseBehaveAs = (outerPromise, nestedPromise) => {
};
};

let then_ = (callback, promise) => {
let andThen = (callback, promise) => {
let outerPromise = newInternal();

let onResolve = value =>
Expand Down Expand Up @@ -204,7 +204,7 @@ let then_ = (callback, promise) => {
};

let map = (mapper, promise) =>
then_(value => resolve(mapper(value)), promise);
andThen(value => resolve(mapper(value)), promise);

let wait = (callback, promise) =>
map(callback, promise) |> ignore;
Expand Down Expand Up @@ -404,7 +404,7 @@ module Rejectable = {

let resolve = resolve;
let reject = reject;
let then_ = then_;
let andThen = andThen;
let map = map;
let wait = wait;
let catch = catch;
Expand Down
4 changes: 2 additions & 2 deletions src/native/repromise.rei
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ let make: unit => (promise('a), 'a => unit);

let resolve: 'a => promise('a);

let then_: ('a => promise('b), promise('a)) => promise('b);
let andThen: ('a => promise('b), promise('a)) => promise('b);

let map: ('a => 'b, promise('a)) => promise('b);

Expand All @@ -33,7 +33,7 @@ module Rejectable: {

let reject: 'e => rejectable(_, 'e);

let then_:
let andThen:
('a => rejectable('b, 'e), rejectable('a, 'e)) => rejectable('b, 'e);

let map: ('a => 'b, rejectable('a, 'e)) => rejectable('b, 'e);
Expand Down
4 changes: 2 additions & 2 deletions src/ppx/bucklescript/ppx_await.re
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ let mapper = {

let open! Ast_helper.Exp;

let then_ = ident(Location.mknoloc(Longident.parse("Repromise.then_")));
let andThen = ident(Location.mknoloc(Longident.parse("Repromise.andThen")));
let x = binding.pvb_pat;
let fun_ = fun_("", None, x, e');

apply(then_, [("", fun_), ("", e'')])
apply(andThen, [("", fun_), ("", e'')])

| _ =>
default_mapper.expr(mapper, e)
Expand Down
4 changes: 2 additions & 2 deletions src/ppx/omp/ppx_await.re
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ let mapper = {

let open Ast_helper.Exp;

let then_ = ident(Location.mknoloc(Longident.parse("Repromise.then_")));
let andThen = ident(Location.mknoloc(Longident.parse("Repromise.andThen")));
let x = binding.pvb_pat;
let fun_ = fun_(Asttypes.Nolabel, None, x, e');

apply(then_, [(Asttypes.Nolabel, fun_), (Asttypes.Nolabel, e'')])
apply(andThen, [(Asttypes.Nolabel, fun_), (Asttypes.Nolabel, e'')])

| _ =>
default_mapper.expr(mapper, e)
Expand Down
6 changes: 3 additions & 3 deletions test/framework/framework.re
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ let run_test = test =>
}
else {
test.run()
|> Repromise.then_(test_did_pass =>
|> Repromise.andThen(test_did_pass =>
if (test_did_pass) {
Repromise.resolve(Passed)
}
Expand Down Expand Up @@ -109,7 +109,7 @@ let run_test_suite: suite => Repromise.t(suite_outcomes) = suite =>
| [] => Repromise.resolve(List.rev(reversed_outcomes))
| [test, ...more_tests] =>
run_test(test)
|> Repromise.then_(new_outcome => {
|> Repromise.andThen(new_outcome => {
new_outcome |> outcome_to_character |> print_char;
Pervasives.flush(stdout);
let outcome_with_name = (test.test_name, new_outcome);
Expand Down Expand Up @@ -180,7 +180,7 @@ let run = (library_name, suites) => {

| [suite, ...rest] =>
run_test_suite(suite)
|> Repromise.then_(outcomes =>
|> Repromise.andThen(outcomes =>
if (not(outcomes_all_ok(outcomes))) {
print_newline();
Pervasives.flush(stdout);
Expand Down
12 changes: 6 additions & 6 deletions test/js/benchmark.re
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ let measure_then = (label, f) => {
/* The callback will be called on the next event loop iteration, after any
callbacks scheduled by f(). */
Repromise.resolve()
|> Repromise.then_(() => iteration(iterations_remaining - 1));
|> Repromise.andThen(() => iteration(iterations_remaining - 1));
}
else {
let elapsed = hrtime() -. start_time;
Expand All @@ -95,7 +95,7 @@ let measure_then = (label, f) => {
iteration(then_ticks);
};

let then_ = Framework.suite("then_", [
let andThen = Framework.suite("andThen", [
test("Js.Promise.then_", () => {
let p = Js.Promise.resolve(1);
measure_then("Js.Promise.then_", () =>
Expand All @@ -106,20 +106,20 @@ let then_ = Framework.suite("then_", [
});
}),

test("Repromise.then_", () => {
test("Repromise.andThen", () => {
let p = Repromise.resolve(1);
measure_then("Repromise.then_", () =>
measure_then("Repromise.andThen", () =>
for (_ in 1 to then_repetitions) {
p
|> Repromise.then_(_ => Repromise.resolve())
|> Repromise.andThen(_ => Repromise.resolve())
|> ignore
});
}),
]);



let suites = [resolve, then_];
let suites = [resolve, andThen];

let () =
Framework.run("benchmark", suites);
Loading