Reasonml let's you call JavaScript functions and pass values around very easily.
As the reason doc says:
Just dumping JavaScript in the middle of your Reason code
Due to JavaScript's very dynamic nature, you might accidentally call a function which doesn't exists or who throws an exception at runtime.
Usually in functional programming languages every interaction with the outside world is handled in a safe way (using a Monad for example).
In Reasonml I was a bit surprise that you are allowed to directly interact with side effects (JavaScript 💔).
Concretely if you typed your function which is invoking some JavaScript code and it throws during its execution, the resulting type won't be correct.
The Either
data structure is a sort of Monad
with two possible values. It represents the result of a computation (left branch) or its failure (right branch).
For example Either a b
is either Left a
or Right b
.
I decided to use a Tuple for the backend of my Either (why is detailed later). Here is its type definition:
type either 'l 'r = ('l, 'r);
For the sake of readability I injected a few JavaScript helpers.
[%%bs.raw {|
function success(v) { return [v, ""] };
function failwith(e) { return ["", e] };
|}];
We need a small interop layer to transform the JavaScript output to a Reasonml value.
type eval =
| Success string
| Error string;
let runtime_to_either v =>
switch v {
| (l, "") => Success l
| ("", r) => Error r
| (_, _) => failwith "Could not parse JavaScript output"
};
I will declare some JavaScript computation examples, this part is up to you since it depends on what your application does.
Looking at the type definitions, it feels very natural; each of these computations return either the result or an error.
let testJsThrows: string => either string string = [%bs.raw
{|
(function (x) {
try {
throw "Error";
return success(x);
} catch (e) {
return failwith(e);
}
})
|}
];
let testJsSucceed: string => either string string = [%bs.raw
{|
(function (x) {
try {
return success(x);
} catch (e) {
return failwith(e);
}
})
|}
];
switch (testJsThrows "test" |> runtime_to_either) {
| Success x => Js.log2 "testJsThrows" x
| Error e => Js.log2 "testJsThrows" e
};
switch (testJsSucceed "test" |> runtime_to_either) {
| Success x => Js.log2 "testJsSucceed" x
| Error e => Js.log2 "testJsSucceed" e
};
Which outputs:
$ node lib/js/src/demo.js
testJsThrows Error
testJsSucceed test
The main issue of the Tuple type is that it only allows one type. That means that the success must have the same type as the error. It defeats a bit the use of the Either.
Ideally, I would use a variant type but its representation at runtime is subject to change in Bucklescript.
My implementation of the Either type was extremely simplified. You can find one in OCaml here.