Recently came to ReScript? Give us your thoughts on error messages!

We’re interested in feedback on the error messages in ReScript, especially from you here who recently came to ReScript. Error messages is a part of the language where we’d like to do more exploration, and understand better what developers are struggling with (and why). Error messages and other related things can hopefully help new users “connect the dots” quicker, and get a less frustrating starting experience with ReScript.

So, please post your thoughts, high and low. We’re interested in the raw things you struggle to understand, to identify areas we can improve, both short and long term. Please don’t hesitate to post all your feedback, even for “simple” things like “I don’t understand why I can’t add (+) thing X and thing Y together, and this error message does not help me”.

Thank you in advance!

4 Likes

Probably not a newbie use-case but here’s an error message I’ve recently spent quite some time trying to understand: “The GADT constructor String of type S.literal
must be qualified in this pattern.”

The problem was because the classify returned the tagged variant that contained GADT literal of unknown, but expected the one of string.

type unknown
type rec literal<'value> =
  | String(string): literal<string>
type tagged =
  | Literal(literal<unknown>)
let classify = struct => struct.tagged

I have difficulty to explain the GADT thing, but the solution was to split the literal type into two - one with GADT and another one without for use in tagged type.

1 Like

I think the one pain area I come across frequently is with

Belt.Option.getExn

My main quip is that it is impossible to tell where the exn is thrown. There is no line number, or code reference provided. If I could pass a custom exception, like getExn(#CUSTOM_ERROR_MESSAGE)

life would be a bit easier

2 Likes

100% agree. Would be solved having exceptions as instance of Error. As a workaround we have:

// OptionExtra.res
let getExn = option => {
  switch option {
  | Some(v) => v
  | None => Js.Exn.raiseTypeError("Unexpected None option.")
  }
}

And eslint config with:

"no-restricted-syntax": [
  "error",
  {
    "selector": "MemberExpression[object.name='Belt_Option'][property.name='getExn']",
    "message": "Use OptionExtra.getExn instead, because it keeps trace in logs."
  }
]

Note: it’s not intended to be caught, so it’s fine not to raise a proper exception

2 Likes

In my team I have noticed that the code example coming first often buries the text description of the error. I wonder if having the summary first would help new rescripters

Here are a couple of new confusing errors I encountered while upgrading a project to v10:

1: A doc comment that’s not attached to anything:

/**
 Here's a comment.
 */
Did you forget to attach `ns.doc` to an item?
  Standalone attributes start with `@@` like: `@@ns.doc`

2: A regex that was valid in v9 but not v10:

let regex = %re("/(1\\/2)/")
FFI warning: Unexpected number, expected the end of input

To make this more confusing, the location of the error is the ending " of the string, where there isn’t a number.

1 Like

I think one of the most difficult error messages to debug is when you have different order of arguments between implementation and interface.

I’ve just spent 15 minutes to realize what’s wrong, and it’s a component with 2 arguments. But I remember I’ve spent around an hour for a component with 15 arguments, had to delete it and start writing from scratch.

6 Likes