Quantcast
Channel: ReScript Forum - Latest posts
Viewing all articles
Browse latest Browse all 2592

My main gripes with rescript (constructive criticism)

$
0
0

i’ve been using rescript since 10.1 or something, quite a bit before async/await was even part of the language. today, i’m basically only using rescript if i can. it’s my favorite language no matter what i’m going to do (except multi-threaded computation, but it’s rare fo rme to do cpu-bound computation today). 99.9% of the time, rescript is a perfect fit if i can model the types properly (and that’s on me).

it has its warts, however, and i really don’t mind. there are, however a few things that i would love to see fixed:

  1. can’t turn off formatting for sections. this is a problem because i know when i need to take control of code alignment. aligning on comments and what have you is often a code smell, but after 30 years of programming, i’m quite confident i know where to draw that line. i would love to have some way of turning off the formatter for a block in a file instead of having to fight the formatter (and just giving up since there’s really no fight to be had except trying to restructure the entire code block)

  2. .resi-files. they’re great, so don’t get me wrong here, however for trivial things like keeping something private or just marking type t = whatever as opaque to the outside world, i don’t want to have to double the amount of source files in my project. keeping module internals private should be the default (i know there’s a thread on this). particularly, the opaque type t thing would be useful for me. we often change type ts in modules as we hammer out new functionality, and often get e.g. string interpolation errors because some type t wasn’t opaque and so type t = string would work fine in string interpolation, but should never have been used as such. this is a recurring albeit small issue for us. maybe opaque type t is possible today and i’ve missed something.

  3. complexity of error messages. this has improved greatly since v10, but it’s still not as good as i would like. the error messages are often exhaustingly long and i find myself more or less reverting to “random” code changes until i get a shorter message that i can bother to read. it works, but i’m sure this area can be improved.

  4. minor: i never get used to .res for source files. this is ingrained in me as resource files, but yeah, this is an artifact from the 90’s i guess. it would likely be even weirder for me to see resx for jsx rescript files.

  5. i still haven’t found a way to export only the modules i want to export from libs/projects. i saw someone mentioning an exports attribute in rescript.json but it seems to not work (at least not the way i would expect it to). our libs have tons of internal code that is basically just infra for the lib itself that shouldnt be exposed, but there seems to be no way to prevent that…

  6. the stdlib is still lacking heavily. it needs a ton of work and many things are still unclear (part of the stdlib is just rescript impl of js counterpartss, part of it lives under the same “namespace” but has nothing to do with js, part of it is mixed). i think a direction should be decided on. for me, i would love to see a rough mirroring of js counterparts, but also a huge growth beyond that. i think this is part of the maturing of the language, it needs to be ready to “go its own way” and not just be a syntactical typed layer on top of js. there’s so much we can do if we allow it to grow further in this direction. (just a few things to give you an idea of what i have in mind, why don’t we have Option.getOrElse that takes a thunk, or Array.zip, or Promise.mapAsync, or even Array.forEachAsync since async/await is now native to the language and so on). we also still have Js. but i think the idea is alerady to remove this? still, confusing.

  7. type inference. there are situations where the compiler should trivially be able to infer a type but just won’t. here’s one such example:

module M = {
  type t = Foo
}

let f = (): promise<M.t> => Promise.resolve(Foo)

it’s clear that f can never return anything other than Foo (through Promise.resolve), but the compiler is unable to infer it. i suspect this is due to compiler internals and it might be complex to implement, but to me as a developer, the type is easy to infer because it couldn’t ever be anything else. yes, we can say M.t in this example but shouldn’t have to because we already said promise<M.t> and beyond that, we might have a switch with differently nested Foo’s and we’d have to say M.Foo in each one depending on how their nesting looks, and it’s less fun when your module names are Dsl_Lang_Libs_Stdlib_SeqOps as is the case in one of our codebases (which by the way is related to the exports problem i mention above).

we could even do:

module M = {
  type t = Foo
}

module N = {
  type t = Foo
}

let f = (): promise<M.t> => Promise.resolve(N.Foo)

at which point the compiler immediately tells you:

Type Errors
[E] Line 9, column 28:

This has type: RescriptCore.Promise.t<N.t> (defined as promise<N.t>)
  But it's expected to have type: promise<M.t>
  
  The incompatible parts:
    N.t vs M.t

so it knows exactly what type to expect, but still won’t infer it. what?

.

please take the above as an attempt at constructive feedback, rescript is the best language i’ve come across for almost any problem i encounter. i rarely or never do frontend work personally but rescript still “outergonomics” almost any other language out there while retaining sound types.


Viewing all articles
Browse latest Browse all 2592

Trending Articles