Skip to content

Commit

Permalink
Adds constructor option to opt-in for trampoline
Browse files Browse the repository at this point in the history
Use polymorphic variants for ease of use and to support the option
of passing a parameterised version in future with user specified executor

Keeps backwards compatibility.
  • Loading branch information
tom committed Dec 12, 2019
1 parent ddfb387 commit 2a5d362
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 15 deletions.
4 changes: 2 additions & 2 deletions __tests__/TestFuture.re
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ describe("Future Belt.Result", () => {
});

testAsync("value recursion is stack safe", finish => {
let next = x => Future.value(x + 1);
let next = x => Future.value(~executor=`trampoline, x + 1);
let numberOfLoops = 10000;
let rec loop = x => {
next(x)
Expand All @@ -330,7 +330,7 @@ describe("Future Belt.Result", () => {
});

testAsync("async recursion is stack safe", finish => {
let next = x => delay(1, () => x + 1);
let next = x => delay(~executor=`trampoline, 1, () => x + 1);
let numberOfLoops = 1000;
let rec loop = x => {
next(x)
Expand Down
4 changes: 2 additions & 2 deletions __tests__/TestUtil.re
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ type timeoutId;
[@bs.val] [@bs.val]
external setTimeout: ([@bs.uncurry] (unit => unit), int) => timeoutId = "";

let delay = (ms, f) =>
Future.make(resolve => setTimeout(() => f() |> resolve, ms) |> ignore);
let delay = (~executor=`none, ms, f) =>
Future.make(~executor, resolve => setTimeout(() => f() |> resolve, ms) |> ignore);
32 changes: 21 additions & 11 deletions src/Future.re
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
type getFn('a) = ('a => unit) => unit;

type executorOptions = [ | `none | `trampoline];

type t('a) =
| Future(getFn('a));
| Future(getFn('a), executorOptions);

let trampoline = {
let running = ref(false);
Expand All @@ -23,17 +25,23 @@ let trampoline = {
};
};

let make = resolver => {
let make = (~executor: executorOptions=`none, resolver) => {
let callbacks = ref([]);
let data = ref(None);

let runCallback =
switch (executor) {
| `none => ((result, cb) => cb(result))
| `trampoline => ((result, cb) => trampoline(() => cb(result)))
};

resolver(result =>
switch (data^) {
| None =>
data := Some(result);
(callbacks^)
->Belt.List.reverse
->Belt.List.forEach(cb => trampoline(() => cb(result)));
->Belt.List.forEach(runCallback(result));
/* Clean up memory usage */
callbacks := [];
| Some(_) => () /* Do nothing; theoretically not possible */
Expand All @@ -46,18 +54,20 @@ let make = resolver => {
| Some(result) => trampoline(() => resolve(result))
| None => callbacks := [resolve, ...callbacks^]
},
executor,
);
};

let value = x => make(resolve => resolve(x));
let value = (~executor: executorOptions=`none, x) =>
make(~executor, resolve => resolve(x));

let map = (Future(get), f) =>
make(resolve => get(result => resolve(f(result))));
let map = (Future(get, executor), f) =>
make(~executor, resolve => get(result => resolve(f(result))));

let flatMap = (Future(get), f) =>
make(resolve =>
let flatMap = (Future(get, executor), f) =>
make(~executor, resolve =>
get(result => {
let Future(get2) = f(result);
let Future(get2, _) = f(result);
get2(resolve);
})
);
Expand All @@ -81,12 +91,12 @@ let rec all = futures =>
| [] => value([])
};

let tap = (Future(get) as future, f) => {
let tap = (Future(get, _) as future, f) => {
get(f);
future;
};

let get = (Future(getFn), f) => getFn(f);
let get = (Future(getFn, _), f) => getFn(f);

/* *
* Future Belt.Result convenience functions,
Expand Down

0 comments on commit 2a5d362

Please sign in to comment.