Hacker News new | past | comments | ask | show | jobs | submit login
ReasonML: Strict, powerful and forgiving (harigopal.in)
165 points by jasim 5 months ago | hide | past | web | favorite | 58 comments

I've been using ReasonML for building complex ReasonReact (React.js binding for ReasonML) apps. With the strict type system, I can code for hours without constantly looking at the browser and it just works on first try. I never feel so happy with coding like that before with Javascript. I would recommend ReasonML for all Javascript developers. Those ReasonML has some downsides that need to be addressed: - Bucklescript constantly releases patch version that breaks stuff, or removing deprecated functions in a minor version. I believe for a crucial piece of the ecosystem like Bucklescript, it needs to have a beta channel so we can test them before official release - Also, there should be more communications between core team and developers, I'm scared for the longevity of the ecosystem. There are no details roadmap of what's coming next or priority of the open issues. There are many important issues (to me) that could increase ReasonML like a lot but never get addressed. An example of such issue would be shipping compiled stdlib in a separate package or have a way that would allow users to integrate ReasonML to their current node.js toolchain without waiting for building the compiler on every build (which could takes more than 3 minutes on slow CI machines)

That been said, I love ReasonML and will continue to invest in its ecosystem. Shamelessly plug, I built https://sketch.sh as a quick playground for ReasonML and OCaml, check it out if you're interested in trying ReasonML

Is lack of ad-hoc polymorphism a real issue for such projects? Everyone who used JS before are accustomed to using overridden methods, or just duck typing, which are form of ad hoc polymorphism too, but Reason/Ocaml lacks it. How does this feel? Or it's easy to code avoiding ad-hoc polymorphism after forming habit for it? Do you use things like function dictionaries to simulate ad-hoc polymorphism?

In principle it seems like a significant limitation but in practice it is rarely an issue.

ReasonML does support parametric polymorphism so it is still possible to write generic code.

Some language features conveniently help to avoid boilerplate. For example it’s possible to have a `Float` module with arithmetic operators and “open” it like this: `Float.(10.0 + 0.5 / 3.0)`.

There’s also a work in progress project called Modular Implicits that will introduce ad-hoc polymorphism.

Even then, I feel like working with Floats vs. Ints in a web browser is almost never an issue. I'd just open Float if I need it because I can't think of any scenario where I'd use both in one module

I'm not familiar with Reason, but OCaml does have virtual methods and dynamic dispatch, and therefore ad-hoc polymorphism.


Now, you can't downcast or cross-cast or do a runtime typecheck easily, but that doesn't mean it's impossible - you just have to use roundabout ways like variants to do so (which also means that you can control which class hierarchies it is available for, and which ones it's not). A class hierarchy combined with an polymorphic variant type to allow runtime type queries can be made as powerful as a runtime typecheck/cast in Java - if more verbose - for those rare cases where you actually need it.


Of course, in practice, most cases where you'd use a downcast or a typecheck in a language like Java, map more naturally to regular variants and matching on them in OCaml.

Actually I like the lacks of it. I know exactly what an operator does without digging deep into the source code. Everything is explicit

We chose ReasonML after a previous project of working with JS, React and Flow. We started using Reason back in May. The plan was to use typescript but after some great experiences with Rust on personal projects, I really wanted a language closer to Rust.

Our team loves using Reason and for me, it's been such a breath of fresh air. Working on front end code is a lot more fun because many of the frustrating things with JavaScript/React has been removed. The compiler is VERY fast and the language is well designed.

With that said, the learning curve is a bit much due to the lack of beginner friendly documentation. If you do not have experience with a ML like language (Rust, Swift, Scala, OcamL, F#) AND React, I think it will be painful. Despite that, it is totally worth learning.

The good news is that the core team realizes this and things are changing fast. React was originally designed for an ML based language and you can see the stark difference between JS React and ReasonReact. ReasonReact really is an eye opening experience.

The whole class component story in Reason felt kinda clunky, but with hooks this will probably not be a problem anymore.

Compared to the Rust compiler's performance, BuckleScript is a moloch, but as long as you don't try to run it on a t2.micro it works okay.

> The whole class component story in Reason felt kinda clunky

See bucklescript-tea for goodbye clunky :)


How has Bucklescript-TEA been in your experience? When would you recommend it and how about JS interop?

For those who don't know, TEA stands for The Elm Architecture, so it's like Elm for Bucklescript or Reason. The library's author is also very active in the Elixir community.

I've only used Elm. I just know of this project, and think it makes sense to cut out the whole React x-mas tree.

I don't understand your second paragraph, are you saying that BuckleScript compiler is slow compared to Rust?

Like a ton of other people here, we've been using ReasonML for about half a year now at Rolltrax [1] and so far I can say I'm ~95% happy with it. I'm a long-time OCaml user but my co-founder had 0 experience in OCaml when we decided to make the switch to Reason. I'll just list off some things we've found using Reason so far:


* Really quick to get productive -- the syntax really does do a great job getting new users familiar with the language constructs (I was very skeptical about this before)

* ReasonReact is very nice and works great out of the box

* Writing bindings is generally pretty easy to do, and we're currently maintaining 4 of these (+ 2 existing binding we've contributed to)

* The language and compiler do a great job at working with you to figure out issues

* Everything feels a million times more structured than normal JS code

* Not sure if this is an up-side, but writing code in Reason is so much smoother than other languages that I feel like it's pushed us to implement more client-side features than server-side ones (unfortunately, this means everything depends on a ton of JS)

* Super readable, I feel like I can skim the code and know exactly what it does instantly


* It's still JS, which means you have to deal with a ton of crap from NPM, WebPack, JS's module system, weirdly written libraries that don't do well with bindings, and things breaking in weird ass ways that you can't easily debug... I think this has been the source of 90% of our bugs so far

* Still no way of getting higher-order components working well, specifically with bindings, which makes it really hard to work with libraries like react-grid-layout

* Polymorphism could still be improved. We occasionally get type signatures that should work in theory but can't because of the object system's limitations, which means we have to give up on the type safety and fiddle with Obj.magic or external JS code

* The JSX syntax is still annoying because you can't use spread syntax with a native element (e.g. div), although eventually I'm gonna get tired enough of doing that that I'll open a PR to fix it

Overall, Reason is by far my favorite way of writing web apps. If that's something you've got to do, give Reason a try. There's still a few pain points but interop with JS is so simple that it hasn't really slowed us down (except when webpack breaks lol)

All of those pain points sound terrible. I want to like Reason and Elm so much but it seems like the amount of time you have to spend getting these things to work with other libraries is absurd. Writing JS is not so hard that I would be more productive in a safer language (beside maybe the very pragmatic typescript) if you account for all of the extra time spent trying to interop with other JS libraries or browser APIs.

Elm seemed like it was taking a pragmatic approach with ports but then they got rid of all of the JS interop code in their package manager, forcing everyone to write their own glue code.

To be fair, issues with bindings have really not been too common and when they appear the fix is just opting out of the strong type checking. 9 times out of 10 when we have a bug the issue is actually something not specific to Reason, like Webpack making 2 copies of React in the output,

We chose ReasonML to build the next version of Learn Anything. So far the experience has been quite pleasant although the ecosystem is still young.


Built a small demo project in ReasonML. It was an enjoyable experience.

The generated JS is readable and well-commented when for instance a record is transformed into an array. Just opening up the generated JS and reading it made for a nice self-check mechanism when I was starting out with the proof of concept.

Avoiding reason-cli and just using bs-platform and reason-vscode together is the way to go in this ecosystem.

The problem with reason-cli is that it can get out of sync with the bs-platform version and fail in unclear ways.

There was quite a bit of pushback against reason at my shop due to the interop between these two packages and trying to do things like have autocompletion fail because of them.

It’d be nice if the upgrade of bucklescript to the OCaml 4.6 drops soon too.

Hi, I work on Reason full time and have been managing the reason-cli releases, and I'm happy to say that we're pretty close to not needing reason-cli anymore because the much better alternative is to _avoid_ global installs and instead model IDE support and as devDependencies of your project. We only have been releasing a global install (reason-cli) because:

1) Until recently we didn't have better alternatives for BuckleScript based projects (but then Reason Language Server came along)

2) We didn't have a good way to quickly install per-project dependencies for the Reason Native workflow. But then we built esy for native workflows which makes it very fast to install large dependencies across multiple projects by using a relocatable immutable package build cache.

Now, there's much less reason to use a global install and global installs will always have the problem of conflicting with project dependencies. I think you're picking up on that fact. Here's to the sandboxed project future!

This kind of stuff is often discussed in the Discord ReasonML channel so if you're ever curious to read the pulse on the direction of dev tools, feel free to join the discussion.

Thanks for the reply! The Discord channel has been very helpful, but I must have missed this sort of thing there.

So ReasonML is actually a Javascript-like syntax for OCaml, and its tool-chain converts Reason code to OCaml, which then uses a tool called Bucklescript to convert that OCaml to Javascript.

All because some people just have to have their curly braces.

I will never stop being angry that this exists. There is no reason for it other than pedantic bikeshedding and Facebook’s mission to proprietize web tech. It does nothing but mangle a perfectly readable syntax and bifurcate the ecosystem of the language.

It's not just about the curly braces - there are many warts about OCaml's syntax, and some of them go beyond mere niceties, and into the territory of making it too easy to unintentionally write code that doesn't do what the author intended. In Reason, it looks like they specifically went after those, e.g.:


If Reason uses == to represent OCaml's =, and uses === to represent OCaml's ==, then how would Reason represent OCaml's === symbol (if it were defined)? Reason provides a way! "Escape" the triple equals symbol!

Yes, such a massive improvement to clarity.

Seriously there is nothing here I hadn't internalized within a day of working with F# or Python. Certainly nothing that justifies forking an entire alternate syntax.

> Yes, such a massive improvement to clarity.

Given that === is not a standard operator in OCaml, I fail to see the problem with clarity.

> Certainly nothing that justifies forking an entire alternate syntax.

You do realize that this is very much a subjective judgment, do you? Syntax does matter. You can argue that it doesn't matter enough to bother, but that's down to personal taste. Given the popularity that Reason seems to be enjoying, clearly, syntax is a sore point for enough people.

In any case, I fail to see the problem in general. Reason doesn't fragment the OCaml ecosystem, really, since it's just a different syntax for the same core language, and libraries etc remain. It's not any different than all the macro libraries for Lisps. Nobody is forcing you to use them, and you can still use a library written in Reason from OCaml, and vice versa. There's no real fragmentation.

And if Reason ever dies, all code that's written in it could just be converted to OCaml using the last version of their transpiler, and maintained as such thereafter.

So, what's the problem?

Nobody is forcing you to use them, and you can still use a library written in Reason from OCaml, and vice versa.

You really should go read up about this, because the actual answer is far more complicated than advertised. Hell, the Ocaml interop section in the official docs isn't even finished yet.

Most of the complexity seems to be stemming from the JS backend, so far as I can see, which is not really Reason's fault. What are the OCaml interop issues when using Reason with the native backend?

"... write code that doesn't do what the author intended."

But this code does not compile. Even if you do not use https://github.com/ocaml/merlin you will always realize it at compile time. What type is y supposed to be to pass compilation?

In practice, your editor's indentation will alert you to the compiler's interpretation of the pattern match.

Making a language feel more familiar to the developers that would likely be using it will probably speed up its adoption and decrease the learning curve for new developers. I don't understand what is so enraging about that.

No one did this for Python.

People still complain about it, but somehow, it became one of the most popular languages on the planet.

And how is it welcoming to newcomers to essentially be upfront asking them "right, now which syntax will you use?"

Unless the answer of course is just to nudge them all in the direction of the one owned by a massive corporation with more money than some countries ...

How's the uptake for ocaml going? Its had 20 years maybe next year will be its year?

reason is fully compatible with ocaml. nothing has been bifurcated. it is not even the first alternative syntax - the "revised syntax" had been around forever. https://caml.inria.fr/pub/docs/manual-camlp4/manual007.html

Reason compiles to Ocaml, yes. Does Facebook actually want you to just use Ocaml? Sure doesn't seem that way to me. Facebook is pushing their own tool, and their own libraries to go with it. Interoperability between Reason, Bucklescript, Ocaml native, OPAM libs, and the various build tools in between is confusing and ill-defined. The path of least resistance just seems to be "stay in our garden... there's no walls technically, but good luck hopping back and forth".

And all because some people are allegedly pathologically incapable of understanding a language without bolting curly braces on it.

It's the most absurd overcorrection for a pedantic complaint I've ever seen in tech.

>>> Does Facebook actually want you to just use Ocaml?

Facebook already uses OCaml.


They use it for a few different projects:


> Reason compiles to Ocaml ..

My understanding is that it can be translated both ways with no difference of semantics.

I know this is getting downvoted a ton, but I certainly felt the same way before using Reason. Even now, I'm sort of undecided about my opinion on it. I love how accessible it makes OCaml (which I think it does, people pay more attention to syntax than they really ought to) but I'm always afraid that it's going to cause native OCaml to be abandoned. Reason can also sometimes make code needlessly complex IMO because it hides some of the really clear aspects of OCaml's syntax. That said, it's improved a ton since it was first released and no longer feels like a weird soup of mixed up languages.

I used OCaml in production for a 2 years and it was a very pleasant experience. Recently I started using ReasonML at my team to implement a Kubernetes configuration tool. It’s surprisingly easy to use ReasonML for backend development with dune and esy.

I really hope Reason is going to become the №1 mainstream language for React apps.

I tried using ReasonML for a blockchain project, and I just couldn't get past being able to express this logic of interacting with a third party library:

``` const contract = new web3.eth.Contract(ABI, contractAddress, {from}) ```

in ReasonML easily, despite spending hours on trying to figure it out. At the end I finally reached to a feature request asking to be able to do 'new' on member functions, and it was in 'planning'.

On the other hand Elm's approach to JS interop seemed to be far superior for quirks like these. Javascript code lives on the other side of the border, and Elm code on this. You don't have to create a type for the whole class of the third party library, just the data you want Elm to manage.

Can you point to the feature request where you asked for 'new' on member functions, please? I would like to correct whatever response said it was in 'planning', because it has been doable for a long time now. Here's some sample Reason that outputs pretty much exactly your above code:

    module Web3 = {
      module Eth = {
        module Contract = {
          type t;

          [@bs.module "web3"] [@bs.scope "eth"] [@bs.new]
          external make: (string, string, {. "from": string}) => t = "Contract";

    let contract = Web3.Eth.Contract.make("ABI", "contactAddress", {"from": "from"});
You can try it out in the interactive playground.

Also, regarding this:

> You don't have to create a type for the whole class of the third party library, just the data you want Elm to manage.

That's exactly how Reason JavaScript bindings work too.

Just to be clear (and I now remember more details), I wanted to do bs.new with bs.send, but it wouldn't work.

I am going to try what you suggested, but keep in mind I did go to the slack channel and tried asking this, none of their solutions looked like yours. Either way, intuitively @bs.new and @bs.send should be able to work together.


I see your problem (which is btw quite different from your original comment ;-) ). In a statically-typed language like Reason it's difficult to model dynamically-generated types like `hello_proto.Greeter`. One way is to use a functor ( https://v1.realworldocaml.org/v1/en/html/functors.html ) but they have their limitations. This is why we have things like graphql_ppx which is a compiler extension to generate types from GraphQL schemas at compile-time. You'd need something like this (or, easier, a ReasonML-targeting gRPC/Protobuf client generator) to solve the issue brought up in GitHub.

Elm has its own infelicities with JS interop. Everything has to be done through ports, which means you have to contort your control flow to work with JS (which is extremely cumbersome if your JS is just pure functions).

For those not familiar with Elm it means you basically have to interact with all JS code through a pub-sub model and can't ever directly call JS code.

Elm's creator has his reasons for why he does this (https://guide.elm-lang.org/interop/ports.html), but it can make calling JS from Elm quite a pain.

You can embed raw js in reason/BS easily with [%raw ] iirc. Check the bucklescript documentaton, where the interop docs are located.

But then I don't get the benefit of ReasonML/BS. I can do that, but then I keep having to do that everywhere I interact with that library (which is essentially the core part of my app).

I do the same thing in Elm, but all that code is living in a JS file, and it can be in Typescript/whatever.

The usual solution to that kind of interop issue is to use wrapper modules and functions.

Doesn't typescript also have variants and similar variant checking? Maybe not understanding what the big deal here is...


For me, Reason's type system seems a lot more focused on what it's trying to do. It's much more minimal than TypeScript's, so you don't end up trying to hack it to fit weird JS types. This also means you get full type inference which is huge.

The other nice things are the fact that it's not backwards compatible with JS so a lot of the syntax is a little more convenient and the module system + object system are the same really powerful ones that OCaml has. Plus, the entire paradigm of the language is so different that it totally changes the way you use React (I remember having all kinds of issues my first time trying to use React just because of how weird some functional stuff is to do in JS, whereas in Reason virtually any program that typechecks is 100% valid for React).

Does anyone have experience with Clojurescript and ReasonML?

I used both a fair amount. I liked both languages quite a bit, but I always found the tooling around Clojurescript difficult to use. ReasonML was a much better experience for me. After using both I think I personally prefer the experience of using a typed language over a dynamic language. This was from the perspective of making UIs with React.

In the context of developing React webapps, what advantages obtain from ReasonML over TypeScript?

I've worked with both.

Typescript is (and feels) bolted-on, and can fail in dozens of ways. ReasonML's type checker is 100% airtight. It also has pattern matching and piping, to help make your code more concise.

Typescript is easy: it's just ES6. ReasonML is (and feels) very much unpolished. Conflicts and overlaps between ReasonML's and Bucklescript's and JS's standard libraries, poor documentation, very flexible but very unintuitive JS interop syntax, frequent updates that break your code, etc.

One big advantage is that ReasonML's compiler is lighting fast. As in, it compiles a big project in an instant. I don't know how it works behind the scenes, but the sheer speed changes how you develop. With JS/TS, you hit save, wait a few seconds for the iterative recompilation, refresh your browser, check your work. With Reason, you code, hit save, immediately get a helpful error message, rinse and repeat, and eventually you open up your browser and everything works 100%. It's pretty cool.

What could be the fastest way to recompile TypeScript that also monitors for file addition and deletion?

So far I'm having a decent experience with pm2 monitoring for any changes or additions of files to restart a ts-node instance but it does make you wait a few seconds or else nginx throws a 502 when the ts-node is still restarting.

Some projects use Babel for everything, including ts->js transpilation. If you go that route (vs using tsc), it may make it more straightfwd to leverage watch, along w/ the rest of the robust babel and webpack tooling that's available. /$0.02

As far as I know, Reason is sound and the pattern matching is very streamlined.

BTW does Reason offer a solution for decimal the decimal precision problem there is in JavaScript?

Nice! Thanks for sharing.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact