Last active
January 19, 2022 02:45
-
-
Save mscharley/8bd90b021c57e20d33ea0a4f4582e2d4 to your computer and use it in GitHub Desktop.
Example of using JavaScript generators in ReasonML.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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