ReScript formatting options

Hi all! I’m fairly new to the ecosystem and have noticed that ReScript auto formats and doesn’t provide a way to configure the resulting formatting.

Is ReScript forever locked into this no-configuration mindset regarding formatting? I’m sure other people besides myself would appreciate having some options. I know having One Way to format is appealing for the standardization-across-codebases reason but to help make ReScript more inclusive, it’d be great to give developers some flexibility. Not everyone finds everything equally readable.

For myself, I’d love to be able to do something like this:

let add
  : (int, int) => int
  = (a, b) => a + b

It’s a bit like how languages like PureScript handle types:

add :: Int -> Int -> Int
add a b = a + b

For myself it’s more readable than how it currently auto-formats:

let add: (int, int) => int = (a, b) => a + b

And I’m well aware that I can do this:

let add = (a: int, b: int): int => a + b

But, for me, I have a hard time parsing the text when types and parameters are mixed in together so I prefer the previous methods for writing out types. Especially with more complicated functions.

The best option for me at the moment is this:

type add = (int, int) => int
let add: add = (a, b) => a + b

But it’s repetitive and more boilerplate.

Thanks for reading this!

I have another example to showcase.

I am using a library with ReScript bindings, which has one rendering function per HTML element, which produces a virtual node vnode corresponding to that element.

For eg., in JavaScript,

label({}, [
  input({
    type: 'radio',
    value: crop,
    checked: true,
    onchange: enteredCropType,
  }),
  text(crop),
])

With ReScript bindings and formatting, this looks like:

Label.t(
    Label.p(),
    [
      Input.t(
        Input.p(
          ~type_=#radio,
          ~value=CropType.toString(crop),
          ~checked=true,
          ~onchange=Action.WithDomEvent.toState(enteredCropType),
          (),
        ),
      ),
      Text.t(CropType.toString(crop)),
    ],
)

To my eyes, the JavaScript formatting looks much better and concise. A similarly formatted ReScript code would look like:

Label.t(Label.p(), [
  Input.t(Input.p(
    ~type_=#radio,
    ~value=CropType.toString(crop),
    ~checked=true,
    ~onchange=Action.WithDomEvent.toState(enteredCropType),
    ())),
  Text.t(CropType.toString(crop)),
])

Yeah pretty much. The project is kinda rigorous on this. There will always be cases where formatting across multiple lines will be weird in one way or another, depending on the usage context.

The layouting is a quite complex topic, and we’d like to keep our focus on having one general format, instead of maintaining permutations of possible configurations.

If there’s anything that could be globally improved (probably harder to do for your specific vnode example), feel free to open an issue on the rescript syntax repo!

5 Likes

The layouting is a quite complex topic, and we’d like to keep our focus on having one general format, instead of maintaining permutations of possible configurations.

Yes. High maintenance is where projects often rot or die out.

I gave my use case a bit of thought. Turns out piping helps a lot! If the array is the first argument (formatting-wise), then ReScript does not mind keeping the opening bracket [ on the same line.

Label.p()->Label.t([
  Input.p(
    ~type_=#radio,
    ~value=CropType.toString(crop),
    ~checked=true,
    ~onchange=Action.WithDomEvent.toState(enteredCropType),
    (),
  )->Input.t,
  crop->CropType.toString->Text.t,
])

Close enough to my ideal vision.

This more or less works on a longer tree too. A too long tree ought to be broken down into functions anyway.

1 Like

That’s unfortunate. Having options can only be helpful for a community of diverse people. Especially when the largest target audience is JS developers (who come from a dynamic land of options and opinions). I imagine overly strict restrictions like this would only hurt ReScript adoption which hurts the community more than a single code format helps it.

Developers spend way more time in their own codebases than in other peoples’. So optimizing for “readability of other peoples’ codebases” instead of optimizing for readability in your own doesn’t make sense to me. The dynamic nature of JS (formatting and ways to do things) hasn’t presented any significant kind of barrier to OSS collaboration in that ecosystem; it’s lush with packages and libraries of all sorts.

1 Like

I think it’s amazing that there is a single language-wide formatting standard that is super easy to enforce. Kind of like standard with full adoption or PEP 8 with built-in enforcement capabilities. Zero bikeshedding!

7 Likes

If you find yourself annotating functions like this a lot, then you may be better off just putting the annotations in interface (resi) files instead.

// file.resi
let add: (int, int) => int

// file.res
let add = (a, b) => a + b

I think most ReScript code doesn’t use a lot of type annotations in implementation (res) files, except to help debug type errors or in a few cases where they’re necessary.

I realize this doesn’t directly answer your question, and I know some people don’t like using interface files at all, but they partially exist for this purpose.

3 Likes

I totally agree on a single formatting standard.

I started with PHP, and in it, you got the PSR, which is a standard way of writing PHP. And it brought a lot of nice feature and tooling for the langage.

1 Like

@ilaitinen @carere

Presumably you’re in the majority with this line of thinking regarding the current community. But that doesn’t speak to how much a strict formatter might affect ReScript adoption. People that are already a part of the ReScript community stayed either because they like the auto-formatter or because they thought the ReScript advantages are worth dealing with the auto-formatter: thus the community is already biased towards the auto-formatter.

We’ll never really know how off-putting it is outside of the community unless we surveyed “developers that considered ReScript and then abandoned the idea” and note the reasons for not adopting ReScript.

I don’t really have any inclination of changing the minds of the current community since everyone here already finds it acceptable at the bare minimum. My main point right now is to increase awareness how something like this can hurt ReScript adoption which hurts the community (more than I think a One Way auto-formatter helps it).

All sorts of programs are empathetic towards users and allow user customization like different fonts, font sizes, contrast options, colors, zoom levels, different ways of interacting with content (like screen readers). A language that restricts accessible and inclusive configurations sounds more like a “language from the past” than the current tag line ("…JavaScript from the Future"). All just so developers can go into someone else’s codebase and have it look exactly like their coding style.

There’s only marginal gains for having things look exactly the same. I think a less dogmatic approach to formatting wouldn’t hurt as much as it helps. Note I’m not necessarily advocating for a Wild West formatting option. Just some sensible configuration options to help accommodate people that aren’t exactly like you.

1 Like

I think some actual data would be very helpful when assessing claims about diversity, accessibility, and adoption impacts of code formatting. Without data, anyone can make claims and counter-claims. For example, I can point out that Go’s formatter is widely regarded as one of its selling points. And it doesn’t allow any customization.

7 Likes

I don’t have any data or research around whether auto-formatters affect diversity, accessibility, and adoption in a programming language. Nor do I plan to do that study. So I won’t be able to give you hard facts around this.

That said, it’s not a revolutionary idea in the UX world that giving users different ways to consume visual data is helpful for a wider range of people. That’s why lists, grids, charts, tables, and even different programming syntaxes, etc. exist. People have different abilities, strengths, and weaknesses regarding how they consume visual data.

If the ReScript community and maintainers don’t find that a compelling reason to add configuration options to the auto-formatter then that’s that. Losing potential community members that find the “everything must be 100% the same” auto-formatter too restricting just becomes an operating cost.

Just some points that come to my mind:

  • At some places there is smart formatting implemented, so that you can put fields of a record in one line or in multiple (when the elements are already on different lines vs. on one line). Same with variant types. There are also some issues which suggest to implement that in more places.
  • There are still some open bugs and other formatting features (like here) which need to be resolved (and make formatting more consistent).
  • The more options you have, the more complex the formatter will get. ReScript aims for lean and fast tooling. As well as maintainable codebases.
  • Last but not least, you can turn off “format on save” completely and do formatting manually.

That all being said, I think some configuration like line width is not ruled out per se. Personally, I would prefer if the config options would stay lean and the formatter would be a little less strict.

1 Like

Thanks for the reply!

How can you turn off “format on save”?

Assuming you are using VSCode, put this in your settings.json:

  "[rescript]": {
    "editor.formatOnSave": false
  },