How to handle the fact that js and js libs are throwing exceptions all over?

so when I work in rust, I usually end up with programs that never crash hard - this is because rust does a good job of propagating error handling requirements throughout your codebase with its result type. I really like that, and obviously try to do the same in rescript.

however, the standard way of dealing with problems in js is just throwing exceptions, which means i have to write wrapper libraries around almost anything that i pull in from the outside world (or i have to mix exception handling and result handling everywhere)

this is one of the major paint points for me with rescript, what are your thoughts on this and how do you deal with it?

Unlike Elm, ReScript makes it easy to interact with external JS. You lose the safety that it brings in Elm, but it makes adoption easier and it allows ReScript to be part of the JS ecosystem and not something separate like Elm.

Of course this means you have to deal with external JS throwing errors. I’m a fan of the ‘tryCatch’ functions from ‘fp-ts’ for handling things that might throw errors. I’m on mobile, but I’ll add some examples once I am on my computer.

2 Likes

You should deal with same situation while you’re working in Rust or even Erlang or Haskell if there is any unsafe external binding or embedded asm

I have very similar approach @jderochervlk mentioned above, isolating core parts from all externals is important to you keep sound runtime.

Elm provides “sandbox” for this by default, meanwhile we should do it optionally by own code architecture. It’s the choice.

Adding some “safety” to every individual external library is very expensive. On the other hand, you can easily make it not a concern of the ReScript module by inversing the dependency. Of course, it is still your responsibility to verify the stability of the entire integrated system, but at least it can be much easier.

IMHO this is a system architecture problem, and not something a programming language can inherently solve (but just hint)

1 Like

Here’s an example function to wrap an external function that might throw an error.

let tryCatch = fn => {
  try {
    let t = fn()
    Some(t)
  } catch {
  | _ => None
  }
}

@module("./foo.mjs")
external fn: unit => int = "fn"

Console.log(tryCatch(fn))
// foo.mjs
export let fn = () => {
    throw new Error('Error');
}

This will never throw and just returns None.

You can also do something similar with a result type if you want to keep track of the error.

You don’t need to do this everywhere, just at a few key points in the program where you don’t want to throw an exception.