First of all, sorry for the long example. I already stripped it down a bit, but I am not sure how to provide a good example.
Today I had my first bad experience wit arrays on rescript. I just happily used what I amused to, which is [0]
and because the compiler didn’t complained I think it was all good. After I got a runtime error about index out of bounds I checked for that basic array access the default ocaml standard library was being imported and used for accessing the array. Very bad surprise I would say.
Without checking on the forum I look at the documentations and I found the only get available on the Js namespace is unsafe_get. So that is what I went for, and I have to say that I’m quite happy with the outcome despite its verbosity. Because the unsafe_get is, well unsafe, I decided to put the type safety manually and say that the external Js code is returning an array of optional strings:
type t = {args: array<option<string>>}
@module("commander") @new external command: unit => t = "Command"
@send external parse: (t, array<string>) => t = "parse"
@module("path") external resolve: string => string = "resolve"
let getArguments = () => {
let program = command()->parse(Node_process.argv)
let fileArg = program.args->Js.Array2.unsafe_get(0)
let fileToMove = switch fileArg {
| Some(path) => resolve(path)
| _ => {
Js.log(`No file specified. Please provide a file name to move`)
Node.Process.exit(1)
}
}
{
"fileToMove": fileToMove,
}
}
Despite it’s “unsafe” nature it forces me to check for the content, and the produced JS is very close to native javascript (despite the weird formatting):
var fileArg = program.args[0];
var fileToMove = fileArg !== undefined ? Path.resolve(fileArg) : (console.log("No file specified. Please provide a file name to move"), Process.exit(1));
Latter I just checked on the forum and I saw that the recommended way is to open belt and use the "native "method to access the array. So I changed it like this:
type t = {args: array<string>}
@module("commander") @new external command: unit => t = "Command"
@send external parse: (t, array<string>) => t = "parse"
@module("path") external resolve: string => string = "resolve"
let getArguments = () => {
open Belt
let program = command()->parse(Node_process.argv)
let fileArg = program.args[0]
let fileToMove = switch fileArg {
| Some(path) => resolve(path)
| _ => {
Js.log(`No file specified. Please provide a file name to move`)
Node.Process.exit(1)
}
}
{
"fileToMove": fileToMove,
}
}
To my surpise, the generated code is almost identical with the only difference that it includes a runtime dependency of belt:
var fileArg = Belt_Array.get(program.args, 0);
var fileToMove = fileArg !== undefined ? Path.resolve(fileArg) : (console.log("No file specified. Please provide a file name to move"), Process.exit(1));
If I have to be honest, I prefer my solution because it doesn’t introduce any runtime dependency and has the same safety and the same Js guarantees.
Is this a wrong approach? why does people recommend belt instead?
Regards