[ANN] ReScript Core - a new, batteries included drop-in standard library

ReScript Core is a new drop-in standard library, intended to be familiar for JavaScript developers and rich enough that you don’t need to reach for anything else for typical ReScript development.

It’s designed to replace the usage of the current Js namespace as well as the OCaml pervasives (global Array, List etc). Belt remains unchanged. This is going to clear up the current standard library situation, which can be quite confusing.

Core ships as its own package. The developer experience will get better continuously (adding proper documentation, doc strings, and so on), but you can start using this today. And if the community agrees on this direction and it stands the test of time, it could eventually become part of the compiler.

Check out the repository for the package here.

Acknowledgements

First thing’s first - a huge thanks to @bloodyowl and the rest of the contributors to rescript-js. This standard library is heavily based on rescript-js. Thank you for your great work!

What does it look like?

Console.log("Hello world!")

let timeout = setTimeout(() => {
  Console.log("Hello!")
}, 100)

clearTimeout(timeout)

let array = [1, 2, 3]

let sum = array
  ->Array.map(x => x * 2)
  ->Array.reduce(0, (acc, item) => acc + item)

let maybeValidFloats = ["1", "1.5", "some random string"]

let validFloats = maybeValidFloats->Array.filterMap(v => v->Float.fromString)


A sample of how code written using @rescript/core can look.

The standard library is primarily a fusion of rescript-js, rescript-promise and parts of Belt. All well liked and heavily battle tested. So, it’s a new standard library in the sense that the package is new, but the contents of it really isn’t.

It moves all modules to the global namespace. Instead of doing Js.String2.length, you’d do String.length. Instead of doing Belt.Option.getWithDefault, you’d do Option.getWithDefault. This is going to make development feel much more natural.

The new standard library also tries to stay as close to JavaScript as possible. But, we’ve made some tweaks and sprinkled in a few additions to make it slightly richer than the standard library of JavaScript, as well as make it better adapted to ReScript where necessary. And, thanks to rescript-js we cover a larger surface of the JS API, with new bindings for things like Map, WeakMap and Set.

We’ve also carefully incorporated useful utilities and modules that we believe are general enough to cater to a majority of developers. A few examples of what this includes:

  • Extra utilities in the Array module brought in from Belt, like flatMap, filterMap, and shuffle.
  • The entire Option and Result modules brought in from Belt. Because options (and to some extent result) is deeply ingrained into the language and ecosystem, they deserve to be first classed.
  • Patrick’s rescript-promise is fully brought in, finally incorporating the refreshed promise module.

Check out all the details on this in the repository.

Cleaning up the current standard library situation

One of the main goals of this initiative is to clean up the current standard library situation. Today, if working with an array, you’ll have 4 built-in options:

  • Array. OCaml pervasives, leftover, shouldn’t be used.
  • Js.Array. Data last, shouldn’t be used, prefer data first.
  • Js.Array2. Data first, use this for zero cost bindings.
  • Belt.Array. Data first and “batteries included”. Use this for more specialized usage.

With this new standard library, just using Array directly is the way to go.

Most importantly, this will make development feel more natural and intuitive if you’re used to JavaScript. Instead of reaching for Js.Array2, or Belt.Array, you’ll just reach for Array. Simpler, easier and cleaner.

What about Belt

Belt will stay as is, and continues to be a great alternative for more specialized use cases. If you’re doing -open Belt and you’re happy with that today, you can just keep doing that.

However, this standard library aims to stay closer to JavaScript in how the APIs are shaped (and what they’re called) than what Belt is currently doing.

Removing the Js namespace

The Js namespace refers to Js.Array, Js.Array2 and everything else under Js.

Having a separate Js namespace is a leftover from when integration between ReScript and OCaml was a lot tighter. Since ReScript is focusing on JavaScript exclusively now, the Js namespace is no longer necessary. Therefore, this is designed to pave the way for eventually removing the Js namespace. All that the Js namespace can do today will be possible to do with this new standard library, which would be available in the global scope directly.

However, removing it wouldn’t happen in the near future. It would live on in its current form for a long time, to let the ecosystem migrate to this new standard library in its own pace.

Migration

We’ll be supplying some basic utilities to ease migration, making most of it automatic. There will however be some manual work involved, but we are going to focus on allowing migration to be gradual. There are more details on migrating here.

Documentation (we need your help!)

As the initial version of the standard library ships as its own package, it’ll lack both some of the docstrings in the code, as well as actual documentation. This will be fixed continuously.

If you’re interested, your help is much wanted. We’d love some help crafting proper docstrings for the parts of the standard library that do not already have them. We’ll get back with more details on how you can help soon.

Wrapping up

Thank you for reading! We’re excited to move forward with this proposal to start clearing up the current situation.

36 Likes

YES Finally! :clap: :clap: I caught wind of this while being nosey on github and have been eagerly waiting for it, the multi stdlib was always a sore spot. I’m happy to see Option and Result made the cut too!

4 Likes

Hey Gabe
Js libraries have lots of mutation…How does it effect Rescript to have those functions exposed directly?
Seems like it invalidates mutable keyword at least? sounds like a lurking problem.

(Having them exposed with some naming is a start…but InPlace less fear-inducing than Unsafe, e.g.)

Thanks
A

3 Likes

I really do appreciate the work towards a new single standard library for js!
While I feel pretty confident choosing one of the possible modules, it’s a huge struggle for some of my colleagues and especially newcomers to rescript. After the announcement in the latest rescript realease blog article, I didn’t expect it so soon.
Kudos to everyone involved in making this lib reality!

That’s actually a really good question. It seems to be an implicit paradigm shift. Are there any plans floating around about an immutable layer for the lib?
Being immutable by default is a strength of the language, in my opinion. I’d very much dislike seeing a lib like immer (js) becoming necessary in rescript.

5 Likes

Can the namespace just be Core or Std?

If I don’t open in bsc-flags is it easier to type Core.Array.map

1 Like

how can I help with the docs?

good point about mutability @mouton

I like Rescript because it is a functional curator for JS. It leaves the Good Parts and hides the rest.

I think its okay to have a core library that binds/resembles to native/mutable javascript api, then an immutable library may be built upon the mutable one, the same way any of the immutable javascript libraries are built upon.

Would be nice to mark this as rescript-core or rescript-js, and leave rescript-std for an immutable one built over the core

1 Like

I think its better to have both in the std lib and make the immutable ones the default (shorter names) and call out explicitly (with eg an inPlace suffix) whenever its a mutation.
I havent been through the repo fully yet to see if thats the approach?

First of all, this is great news. Thanks for all the hard work!

That said, I tend to agree with everyone wanting immutability by default. And I find it rather inconsistent that inside the same library arrays are mutable, but maps and sets are not. Moreover, I find the JS array interface pretty inconsistent: for instance, reducing creates a new array, but sorting mutates the old one.

5 Likes

Maps and sets are just bindings for JavaScript Map and Set which are mutable, too. Probably the signatures of Map.set/Set.add should be changed to return unit so that they are not confused with immutable updates.

I think the idea was that the mutating functions in Array

  1. return unit
  2. are named ...InPlace unless it is obvious that they are mutating, like for push and pop

So we currently have

@send external sortInPlace: (array<'a>, ('a, 'a) => int) => unit = "sort"

I guess an immutable sort function should be added.

2 Likes

Hi and thank you everyone for providing your feedback about immutability defaults! I’ve opened a GitHub issue on the repo with some pretext where we can continue the discussion on the way forward: [Discussion] Immutable defaults for mutable JS API:s · Issue #23 · rescript-association/rescript-core · GitHub

Let’s continue this particular discussion there.

3 Likes

This is easy to do locally in your project, either via include RescriptCore in your own Core.res, or manually aliasing the modules you want access to.

For those of you interested in helping out with documentation/docstrings, there’s now an issue on the tracker to coordinate that work: Documentation and docstrings · Issue #25 · rescript-association/rescript-core · GitHub

1 Like

An update on Core:

We’re nearing a new release, with among other things a large set of docstrings introduced.

A big big shout out to everyone who’s contributed and partaken in the discussions so far. We’ve got some fantastic, thorough and valuable contributions. Community working together at its best!

13 Likes

First class support for Core has landed in gentype, and will be out in the upcoming 10.1.3: https://github.com/rescript-lang/rescript-compiler/pull/6019

9 Likes

After a large effort from many contributors, 0.2.0 of Core is now released. Adding a large amount of docstrings, and fixing inconsistencies/bugs. Check out the full changelog here: Release 0.2.0 · rescript-association/rescript-core · GitHub

Next up is continuing work on adding docstrings, and then work on documentation extraction/generation, and the editor tooling.

Thank you all contributors for your efforts!

17 Likes