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!

6 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.

2 Likes

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

3 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

1 Like

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.

4 Likes

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.

13 Likes

MyLib is a dead module as all its items are dead.

I see that often in VSCode hovers. Not only do I not know what that means, but I’m also a little scared.

2 Likes

I think the one error that I face very often during refactoring is the “something somewhere” error, and while I understand what causes it now, there’s been more than a couple times I’ve had to go through and add explicit types all over the place to track down why something somewhere thinks this should be something else

Don’t know if it’s possible, but if the error message can include a small snippet of where the assumption is being made it would really help understanding why that’s happening

4 Likes

“Dead” means it’s not being used anywhere nor contain side effects, and can/should be deleted.

‘unused’ is not good enough?

“dead” is a specific programming term, used often in the phrase “dead code analysis”, so I think it was probably used here in that context. Precision of language is quite important here because the compiler can’t know your future plans for a given piece of code.

1 Like

I was looking for this feedback. I’m having a hardtime with this, so many times. This is not helping in my use case, I see “signature mismatch” and I end up comparing myself directly in the codebase.

Could color highlighting be a thing for specific parts of the signature mismatches?
I believe the error message itself is explicit enough (and with the whole signatures printed), it’s just painful to deal with.

2 Likes

That’s a good point. Could you give a few concrete examples? That would be helpful.

This error message has little sense to me:

This functor has type
(
T: {
  module Query: {
    module Raw: {
      type t
    }
    type t
    type t_variables
    let query: string
    let unsafe_fromJson: Js.Json.t => Raw.t
  }
},
) => {
let call: (~variables: T.Query.t_variables) => Promise.t<
  result<T.Query.Raw.t, string>,
>
}
The parameter cannot be eliminated in the result type.
 Please bind the argument to a module identifier. ReScript

The code:

1 Like

To expand on my previous thought, this one keeps catching me off guard because I forget the ++ for strings

let greeting = "hello"
let sentence = greeting + "world"
// This has type: string Somewhere wanted: int

I’ve came across another confusing error message. I think adding quotes for the field name will make the error message readable, but maybe we can make something more.

https://rescript-lang.org/try?version=v10.1.2&code=C4TwDgpgBAhlC8UDeAoKUBmBLCAbAJgFxQDOwATlgHYDmANGpjgcWZbQwL4pA

In my case it was even more confusing, because I was using the type spread, and the code looked like this:

type a = {
  field: string,
  ...ctx
}
// ^ Two labels are named field

That looks like it should be a very easy fix.

Should it be something like this?

The record type "a" has multiple fields called "field". To fix the error rename duplicated fields or remove them.

Here’s a suggestion: Improve error message for duplicate labels by zth · Pull Request #6415 · rescript-lang/rescript-compiler · GitHub

1 Like