Why Belt vs Jane Street Base?


I’m barely getting started. I was searching Belt for functions to transform between exceptions, result, and option. I continued searching and found Jane Street’s Base already had them. I especially love how it’s built up monadically with functors ensuring a consistent and broad shared API surface between “container types”. That was one of my big complaints with Elm’s standard library a couple years ago. It’s also built with ppx_let for async in the future. I assume Base was already considered. I just wondered historically why Belt doesn’t reuse it?


A few of Belt’s motivations are spelled out in the docs, but I think the one that most splits it from Base is:

  1. Better performance and smaller code size running on JS platform

which is not a main goal of Base, and which has systemic design implications. For example: the Belt authors specifically call out functor-based collections in the OCaml sodlib as making dead code elimination much harder.


Thanks @Riwsky that sounds like a plausible and reasonable context.

It’s a bummer 1) Base is much more comprehensive akin to Prelude. For instance I couldn’t find a corollary to either Option.try_next or Result.try_with in Belt. I find Container functions like fold_until generalizing List.every and List.some handy too. I suppose they aren’t onerous to write with vanilla pattern matches and/or recursion. I wonder is the API kept intentionally small? I assume the two libraries couldn’t interop over duplicated types like result.

More vexing to me is 2) inconsistency between available functions. For instance Container's iter appears as List.forEach yet neither in Option nor Result. What if there were Container-like functors available or even confined to tests just to prove conformance of their monomorphic versions to some interface? Like don’t use them in real life unless you’re a library author checking you implemented a type the Reason way.


@texastoland I’ve raised the need for having some standardized interfaces in this PR: [Proposal] Belt Monads

Would be nice to see your concerns voiced there too :slight_smile: perhaps we can come up with a pragmatic decision that provides correctness and consistency across the standard library, while remaining minimalistic and not forcing people to take a deep dive into Category Theory to work with it.


Exactly! I’ll take a little time to grok it and chime in there.


@Riwsky I mocked something up and don’t see any difference in JS between modules with/without functors. It’s not meant be to be a performant implementation nor a proposal for specific functions or operators (on the contrary I prefer Belt’s philosophy). Even if functors were bad unfancy signatures would still suffice to guarantee API consistency and availability across types.

@ostera I think it’s the right idea but:

  1. They aren’t built up hierarchically i.e. Functor -> Applicative -> Monad.
  2. I don’t think operators would fly within the community at all.
  3. Prior art exists for implementation. What’s lacking is a core stakeholder to champion a common set of signatures for standard types (for libraries to implement too). The precise type classes are less important to me.

I guess the right way forward is a issue in BuckleScript?


Absolutely intentional. The aim was to not force people into understanding what constitutes a Monad from a categorical standpoint but rather to provide the least common denominator required for establishing well-behaved primitives in the library (Option, Result, Stream, Future, etc).

Also true! That’s why I included the more Java/Scala friendlier names such as flatMap for >>= and in the PR I propose orElse for <|>.

That’s what I’m trying to get started :smile: please voice the concerns on the PR. Perhaps that way we can gain momentum and define at the very least some interfaces we can all rely on.


@ostera would you be down for opening an issue and working back toward a PR? I view the steps as:

  1. Convince a maintainer that signatures of some form are necessary.
  2. Agree that any and all names are subject to change.
  3. Decide to what degree such signatures should reflect type classes.
  4. Decide whether to imitate an existing library.
  5. Decide what functions to include, functors or no, hierarchy or no. From my perspective the last is a given. It’s an implementation detail that gives you functions for free.
  6. PR/profit!


I have a draft of an RFC on interfaces that I’ll publish today :smile: I’ll get you a link as soon as it’s live.


@texastoland RFC 0001: Standard Library Interfaces — let’s get this discussion started! Feel free to submit a PR with changes, or even a second RFC that builds on top of it :slight_smile:


:raised_hands:t4: @ostera just read it. Wow it was really superbly written. I understand your motivation for not breaking up the hierarchy. I say present that part as is for discussion. My inner Haskeller complains not all monoids are monads ect. but that’s too edge case to matter. Maybe Foldable should include Traversable? Anyway I think this is the perfect starting point. Thanks for making it!


What needs to happen to move forward? Excerpt from Real World OCaml:

One problem with Fqueue is that the interface is quite skeletal. There are lots of useful helper functions that one might want that aren’t there. The List module, by way of contrast, has functions like List.iter, which runs a function on each element; and List.for_all, which returns true if and only if the given predicate evaluates to true on every element of the list. Such helper functions come up for pretty much every container type, and implementing them over and over is a dull and repetitive affair.

As it happens, many of these helper functions can be derived mechanically from the fold function we already implemented. Rather than write all of these helper functions by hand for every new container type, we can instead use a functor to add this functionality to any container that has a fold function.

We’ll create a new module, Foldable, that automates the process of adding helper functions to a fold-supporting container. As you can see, Foldable contains a module signature S which defines the signature that is required to support folding; and a functor Extend that allows one to extend any module that matches Foldable.S:

Also relevant to a recent tweet about standardizing Promise function names.


Have you seen bs-abstract!!