Skip to content

Instantly share code, notes, and snippets.

@mscharley
Last active January 19, 2022 02:45
Show Gist options
  • Save mscharley/8bd90b021c57e20d33ea0a4f4582e2d4 to your computer and use it in GitHub Desktop.
Save mscharley/8bd90b021c57e20d33ea0a4f4582e2d4 to your computer and use it in GitHub Desktop.
Example of using JavaScript generators in ReasonML.
/* Note, this is only for library interop code. This is *not* a good way to do things in pure Reason code. */
module type GeneratorType {
type value('a);
type t('a);
type fn('a) = unit => t('a);
let valueGet: value('a) => option('a);
let doneGet: value('a) => bool;
let next: t('a) => unit => value('a);
let return: t('a) => Js.Undefined.t('a) => value('a);
let throw: t('a) => exn => unit;
let fromNext: (unit => option(option('a))) => t('a);
}
module Generator: GeneratorType {
[@bs.deriving abstract]
type value('a) = {
[@bs.as "done"] done_: bool,
[@bs.as "value"] value_: Js.Undefined.t('a),
};
let valueGet = v => v->value_Get->Js.Undefined.toOption;
let doneGet = done_Get;
[@bs.deriving abstract]
type t('a) = {
next: unit => value('a),
return: Js.Undefined.t('a) => value('a),
throw: exn => unit,
};
/* fn(int) is the type of the following javascript: function* () { yield 1; } */
type fn('a) = unit => t('a);
[@bs.send]
external next: t('a) => unit => value('a) = "";
[@bs.send]
external return: t('a) => Js.Undefined.t('a) => value('a) = "";
[@bs.send]
external throw: t('a) => exn => unit = "";
let simple = next => {
let iterator = t(
~next,
~return = v => value(~done_=true, ~value_=v),
~throw = exn => raise(exn),
);
/* Necessary, to flag to JS land that this is iterable. */
/* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols */
[%raw "iterator[Symbol.iterator] = function() { return iterator }"] |> ignore;
iterator;
};
let fromNext = next => simple(_ =>
switch(next()) {
| Some(v) => value(~done_=false, ~value_=Js.Undefined.fromOption(v))
| None => value(~done_=true, ~value_=Js.undefined)
}
);
}
open Generator;
let jsGen: Generator.fn(int) = [%raw "function *() { yield 1; yield 2; yield 3 }"];
let gen = jsGen();
Js.log("Running the JavaScript generator:");
let i = ref(gen->next());
while (! (i^)->doneGet) {
Js.log((i^)->valueGet);
i := gen->next();
}
let reGen: Generator.fn(int) = _ => Generator.fromNext({
let i = ref(0);
() => {
if (i^ < 3) {
i := i^ + 1;
Some(Some(i^));
}
else {
None;
}
}
});
Js.log("Running the Reason generator:");
[%%raw "for (var x of reGen()) { console.log(x); }"];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment