Gleam Trick: Resultify
Posted on .
Here's a short Gleam trick – that I'm actually removing from Scriptorium since it's no longer used there – but that I think deserves a little post anyway.
When you call external functions in the JavaScript target, it is likely they might throw an exception. At least much more likely than in the Erlang target, due to the differences in API design and error handling in the languages. A thrown exception is annoying, since you cannot catch it with Gleam tools directly. This is where the helper comes in.
First we define the Gleam module:
/// A result (of type a) from an external function that can also raise an error
/// (of type b).
///
/// A function that "returns" this type should not be called directly, instead
/// it should be called via `resultify`, which converts the result or exception
/// into a Gleam result.
pub type CanRaise(a, b)
/// Convert a callback function (that should call an external function) from one
/// that can raise to one that will return a Result.
@external(javascript, "/path/to/ffi_exceptions.mjs", "resultify")
pub fn resultify(callback: fn() -> CanRaise(a, b)) -> Result(a, b)
Pair it with this ffi_exceptions.mjs
:
import { Ok, Error } from "./gleam.mjs";
export function resultify(callback) {
try {
return new Ok(callback());
} catch (err) {
return new Error(err);
}
}
Now let's annotate an external function with the CanRaise
type:
@external(javascript, "node:fs", "readFileSync")
fn do_read_file(path: String) -> CanRaise(Buffer, FSError)
This is actually a little white lie. The function actually returns Buffer
or throws, but since
CanRaise
is an external type that we cannot use in Gleam land, it's not that bad of a thing.
It does require some discipline to not use this function without resultify
, though.
The final step here is to call the external function with resultify
:
let path = "..."
use contents <- result.try(exceptions.resultify(fn() { do_read_file(path) }))
With resultify
we can call any such external function and use it with the normal Result
workflow in Gleam side.
Let me know if you think you have a better way of doing this. I might feature it on the blog!