Upon reading the docs I came across something I hope someone can comment on. On the Converting from JS page, it says in the third bullet point: Doesn’t force you to search for pre-made binding libraries made by others. ReScript doesn’t need the equivalent of TypeScript’s DefinitelyTyped .
Yes of course it’s still great to have some starter bindings for popular libraries, or piggyback off existing work that’s out there. ReScript has a minimalistic Package Index to search for some pre-existing bindings, but the workflows are kinda different to e.g. DefinitelyTyped / TypeScript due to the nature on how ReScript works.
In ReScript, bindings can be written in different styles and flavours, introducing extra runtime code, some of them adding complex typing layers, or targeting a specific subset of the API (and tailor specific types for them. e.g via configurable modules via Functors etc.). It’s very hard to draw the line on what is an idiomatic binding. Many feature-rich bindings tend to get a little overwhelming and you spend a great amount of time trying to make sense out of the APIs (oftentimes reading interface files), sometimes keeping the equivalent JS docs open for comparison, because some do change the names of functions, or fine tune parameters. At least for me, it was often easier to just copy paste the interesting parts and adapt it before trying to stick to upstream.
The ReScript type system is also strict and isn’t just “types on top of JS”, it’s more than that, which means that writing generally applicable bindings is a very tough job, with a lot of decision making on what APIs to expose, and in what format (and how configurable these settings should be). TypeScript users have an easier time there, because they can just blob any types whenever they hit some roadblock in an interface, and they also have all the JS semantics at hand without thinking about interop.
So I guess the general advice that is being made here in the docs is that people shouldn’t feel afraid writing adhoc external statements / binding files as they go. It’s an essential skill that one has to acquire to really get the most out of the language.
As an example on how I distribute some of my work: In my rescript-nextjs-starter repo, I ship a basic Next.res binding and give the advice to not be afraid of changing / refining it for their use-cases. It would get out of hands if I’d go and try to write a feature complete Next binding, without even knowing what those bindings are being used for.
As an example, there are some modules within the reason-react repository that are not completely runtime free (all the modules starting with ReasonReact), e.g. the ReasonReactErrorBoundary has some utility functions, or even some utility external bindings in there (to allow quick dom api access) that will generate some JS when compiled.
We also learned over time that it’s kinda useful to create certain specialized bindings (such as querying a dom node by id just for mounting the React app), even though it’s theoretically duplicating code / mixing concerns with other libs such as bs-webapi. It’s a tricky philosophical problem.
I’m afraid that example did not explain much to me, but I concede that is due to a lack of knowledge of how ReScript works on my part. Your first answer cleared up some of my confusion. I think I’m gonna have to play around with ReScript to experience the difference in workflow (compared to TS) as you pointed out. I’m doing TS on my day job and it’s quite interesting to learn a bit of ReScript on the side.
Yeah hard to explain in theory. When starting out with ReScript, it really helps keeping the generated .js file open next to your .res file and compare the output when writing code and bindings. You will see what I mean when you try to interact with your first JS library or with the browser webapi.
Figuring out how to train people in the complexities of ReScript bindings is something we’re going to tackle at my day job in the coming months. I’m hoping if we generate good training material I can make a case to post it publicly where the community can benefit from it.
@spyder, I’ve thought about writing a blog series that explains some of the common issues we run into when writing bindings, and some different approaches for overcoming them. For example…
How to represent highly polymorphic functions
How to represent complex “options” objects
How to handle a function where one argument determines the type of another argument.
When to omit certain kinds of bindings.
Might be useful for the community, but the task seems daunting for one person to do in a reasonable time. Collaboration would certainly help with that. So with that in mind, I’d be happy to contribute to something like that in my free time.