NODEJS callback-style programming entered the JavaScript developer's toolbox a few years ago and brought with it the term 'nodeback', short for (I guess) 'node callback'. The idea of this callback is that it gets called with upto two arguments: an error value xor a success value, representing that the previous operation failed or succeeded and letting the programmer decide what to do next. For example:
fs.readFile('/etc/passwd', (err, data) => {
if (err) throw err;
console.log(data);
});
Although the nodeback style of programming has mostly been superseded in the JavaScript world, thanks to the advent of promises and async/await, developers still occasionally have to deal with it.
The problem with this callback is that either of the parameters might be undefined
, and you have to, every time, manually implement the logic of the callback in such a way that the data
is not accessed if there is a non-empty err
, and vice-versa.
In strongly, statically-typed languages like ReasonML, we have the ability to wrap up this unsafe API, with a slight runtime overhead, in a much more type-safe and ergonomic API. Here's the wrapper:
let nodeback(f) = (. err, result) =>
switch (err, result) {
| (Some(err), None) => f(Js.Result.Error(err))
| (None, Some(result)) => f(Ok(result))
// Throw if APIs break nodeback 'guarantee':
| _ => invalid_arg("Nodeback arguments invalid")
};
You can use this like so (with a hypothetical Node.Fs.readFile
binding):
Node.Fs.readFile("/etc/passwd", nodeback(fun
| Error(err) => raise({j|$err|j}) // Can't access data in this branch
| Ok(data) => Js.log(data)), // Can't access err in this branch
);
The way nodeback
works is, it takes as input a type-safe result
-handling function and converts it into a nodeback (formatted to highlight the input and output):
let nodeback:
(Js.Result.t('a, 'e) => 'b)
=>
(. option('e), option('a)) => 'b;
You can use the nodeback
wrapper to get its type-safety benefits, while passing the JavaScript side the nodeback that it expects.
[EDIT: see correction and full working example in the comment below]