BuckleScript 8.1: new syntax option


Some clarification on the leadership:

I am mostly in charge of things after parsing. I am not very interested in the syntax itself but I see some technical merits from the new parser:

  • It is hand written which gives us flexibility to improve the error message

  • It has zero dependencies, so it is really easy to embed into bucklescript itself and delivers better performance. The integration of refmt used to be a headache that I gave up and called it from an external processor and the sync up relies on community contribution – with the new parser I would be happy to do the sync up work.


In the spirit of sharing community reaction, I would like to write my opinion on this.

I don’t have strong feelings about new syntax changes, if anything, let’s keep them coming (just maybe in a more planned way and on a predictable schedule).

What really worries me though is that this syntax experimentation feels out of place.
I would rather prefer being ‘annoyed’ by parens and semicolons, but have a better async story.

Or see bucklescript move towards better integration with the rest of the ecosystem: esy support for packaging and use of native ppxs, potentially switching to dune for builds, etc. Until vscode-ocaml-platform came around, I didn’t try bringing esy into bs projects. But now I see a million possible ways to have a great native tooling and infra story smoothly integrated with frontend/edge.

So my take is let’s bring bucklescript and ocaml native closer, not the other way. As others pointed out, bucklescript becoming a niche language with insular governance and incompatible with the larger ecosystem brings a risk of people reviewing their technology stack for future projects.


The new syntax is great! But as others pointed out, is it possible to
avoid incompatibility the new syntax and its tooling has introduced?

I understand Bucklescript leadership has always battled conflicting requirements.
Balancing the JS/web community’s requirements with a language as old
as OCaml is not easy. But I do want to add here that there are folks
trying to bridge them.

From what I understand, authors of OCaml tooling want to accommodate Reason
community. Dune supports Reason syntax out of the box. Maintainers of
vscode-ocaml-platform were welcoming when I tried adding bucklescript
support to the plugin. But with the new direction, I find it hard to
initiate such reconciling efforts (for instance, without a merlin
plugin for the new syntax, ocamllsp wont work).

I really want to urge Buclescript leadership to reconsider this divide.

Apologies for making another post less about the syntax and more about
the broader direction. The care and pain you’ve taken to improve the
experience (for instance dissuading DSLs hard to reason about,
avoiding unnecessary currying with fun) is really appreciated. I
want to acknowledge this as well as the challenges in reconciling
conflicting voices/feedback - some of us have been going through this
as well :slight_smile: I think broader unification efforts will be really


I won’t have time to read the whole discussion until tomorrow, so apologies for posting this out of context, but for now I would just like to say

This seems like it will fork the community pretty hard. “tasteful” is just asking for bikeshedding.

It will block those of us who make extensive use of custom infix operators from upgrading, unless the plan is for bucklescript to permanently support both reason and “bucklescript reason” which will be so confusing I don’t even know where to begin.


@spyder one last reply before I go: these decisions can be remediated.


I try to share my early thoughts about this change:


  • :+1: Removal of semicolon: I like this, but leaving them optional may result incosistent codestyle among community, maybe completely remove them?

  • :+1: Less parentheses, less noisy code

  • :+1: Type arguments from from ('a) to <'a> : To be honest I may perefer parens personally since <T> is java-ish but it’s also more similar to ts / flow 's type arguments and also make distinction from function application. this change makes sense to me.

  • :+1: Interpolated strings: This is like scala’s string interpolation, I like it, more clear syntax.

  • :+1: Polymorphic variants: I have to play around with them. currently it lgtm.

  • :-1: Arrays and Lists: I Don’t like Arrays to be first class containers. Wrote more on this topic

  • :+1: Exception Handling: Nice change, more js friendly. I think it’s even more like ocaml’s try e with p

  • :-1: No custom infix: This is a language feature I really like. they are libraries out there using this to make easier / readble DSLs. I know and agree it can be abused and make codebases cryptic (just like single letter function names can, should compiler stop you from declaring them?) but this should be considered in project / problem / team context. a compiler option for enable/disable custom infix would be nice

  • :+1: Object access and implicit Js.t: no comment

  • :+1: New multiline strings: no comment

  • Currently I have no certain idea about other syntax changes, I may write about them later

The error messages as other folks said are super nice!

To be honest I don’t like this change to result in a new language/syntax option for bucklescript or ocaml platform in long term. I like to discuss about each part of the change, normalize them and make them compatible with reason/refmt 4 if possible. I think this approach has some benefits including but not limited to:

  • Sharing code between native / js. as being discussed some teams using this as well as some libraries like graphql ppx
  • Maintaining current ecosystem and help it grow, rather than creating a new ecosystem (Imagine this case: What should i google when I face a problem especially as a newcomer? reasonml, reasonscript.)
  • Separation may result in making bucklescript more javascript-ish (For example making array, a mutable data structure, first class container). I don’t want bucklescript to be just a better typescript

Thank you for all the effort :heart: :rocket:

P.S: It would be nice to have individual topic for each change to discuss like list and polymorphic variants topics


How to use FCM in the new syntax?

It’s syntax error:

module type X = {let x: int}

let fn = (~x: (module X)) => {
  let (module X) = x


Reddit mentioned they are out…


wat :anguished: why?.. This is literally the only way to pass module to a function. Dynamic imports, component as props. How to deal with it w/o FCM?


Let’s wait for confirmation. I would be surprised if FCM are really being dropped by Res syntax.


Jordan’s comment on discord “The BS syntax … trims out many of the advanced ocaml language features.” suggests that it might be correct.
Do FCM’s compile terribly to js, is that why they may be out?

I’m slightly worried we may be getting a lot less of a functional language than I first appreciated, no infix operators, arrays by default, the reckless currying comment, etc… hopefully that isn’t the case.


I think FCM is still a thing in the new syntax, since there is a function in napkin called parseFirstClassModuleExpr: https://github.com/BuckleScript/bucklescript/blob/9551c6975809961a37a3341309790889b4e949c5/jscomp/napkin/napkin_core.ml#L1820

Edit: According to the comment in the code, it seems the syntax is like: module(module-expr) or module(module-expr : package-type)

Btw: This was the first time, I’ve been looking into this source and I found it right away. I think this is some evidence for the quality of the work which has been put into napkin.


That’s really cool. Love this change. It’s been discussed for Reason syntax too, I hope it gets in :crossed_fingers:


Haven’t looked at all the features, but wanted to say that you have no idea how thrilled I am to have expressions and Unicode in string interpolation. And without all the {j|..|j}. Thank you!


@woeps well this can just make me cry. Yes that’s a very important goal we’ve been aiming for.
And yeah the first class module syntax is that. I’ve forgotten to document it. It’s really amazing that you found out by reading the parser code.

@jdeisenberg thanks. It’s about to get better I think. We’ll finally bridge this gap.

Edit: first class module documented now.


Alright, this compiles, phew!

module type X = {let x: int}

let fn = (~x: module (X)) => {
  let module (X) = x


can you file an issue? I think the parens are still too many, ideally it should be

let fn = (~x : module X) => {
   let module X = x


You can also use the new unpack that replaces (val binding)

module type X = {let x: int}

let fn = (~x: module(X)) => {
  module X = unpack(x)


Sure! But in the light of the changes, I’m a bit confused in which repo it should go :sweat_smile: Guessing it belongs to BuckleScript now?