There is a specific niche that BuckleScript and Reason can fill, and that is the sort of front-end projects that use React+Redux+ImmutableJS+TypeScript/Flow.
Instead of bolting on typing into Javascript (Flow and TS both do a good job of it btw), you could simply use a language that was designed with it. There is a huge difference in native adoption of such a core language feature vs using a transpiler that ultimately leak Javascript semantics.
If you're using immutable values through ImmutableJS-like libraries, or by just being careful to not mutate stuff - then you should try working in a language that has immutability baked in. Having confidence that you've been careful to not mutate anything in your code vs using a language that simply disallows it in the normal course of programming brings a tangible difference.
You can call BuckleScript/Reason as just a better version of Javascript with all the good things baked in. But it definitely is much more - you get to use Variants (Sum/Union types in other parlance), and more importantly, writing value-based immutable code (instead of reference based mutation) lets you rely on equational reasoning to understand your code. Your functions are pure and can be seen as though they are equations, and your whole program is a composition of these equations.
A good thing about BuckleScript/Reason vs transpiled languages like CoffeeScript is that we're fully free from Javascript's semantics. You are solidly inside OCaml, and Javascript simply doesn't leak through. You don't have to worry about JS scoping rules or how `this` works, or how ES6 classes are ultimately prototype-based. Except for a well-defined foreign-function interface that lets you use all of npm libraries or your existing JS code, once you are in the typed immutable world of OCaml, then that's all you need to think about.
All of this applies not just to BuckleScript, but any Typed FP that runs on the browser - Elm, PureScript, and I believe Scala.js as well.
But one big advantage (or disadvantage, depending upon how you look at it) of OCaml/Reason/BuckleScript is that it allows mutation if you need it. You can start out writing purely imperative code just as you would in Javascript, and slowly adopt immutable programming as you get comfortable with it. You can also rely on mutation when you need to optimize specific parts of your code.
TypeScript is far more powerful than you think. Between const, readonly properties, Readonly<T>, ReadonlyArray<T>, readonly indexers, and type safe object spread {...} via keyof T, TypeScript provides the tools to write immutable code, fully checked by the compiler, without paying runtime costs.
It also has tagged unions, product, and sum types, nullability, and extensive capabilities for typing 'this'. So I'm left to ask what exactly is the unique advantage of writing JS in OCaml? Because it's not immutability. Remember too, that OCaml has plenty of warts.
I use TS myself. It helped make front end development OK for me again. But man, one really misses some things.
I recently picked it up again and struggled to remember how to properly do pattern matching and exhaustive checks, until I realized that there were more hoops to jump through than at a circus convention. A friend ultimately pointed me to https://pattern-matching-with-typescript.alabor.me/ but that made me weep.
Then I remembered how I hadn't been able to figure out how to use object spread to perform variable assignments of all members from an object returned from a function, with compiler verification that no object members had been forgotten. That may be possible now, I'm not sure. I don't think it was possible last I battled with it.
Don't get me started on the ergonomics of immutables. Again, this may have improved, and no one would be happier about that than I.
But all in all I feel that TS is not what I'd like to use for front end development. However, it's where the community and development effort is at, and I feel like I could never convince my colleagues to go with BS/PS/Reason/anything else. I haven't even been able to push them from JS to TS yet.
If someone wants to show me the error of my ways, I'd love it, since I'll probably be using TS for some time to come.
* 'readonly' support, while nice to have, is mostly broken. It's just way too easy to coerce a readonly value into a non-readonly one, especially with nested objects.
* There are no proper sum types. You can emulate tagged unions with a type field as a tag and match on that and get good compiler support for the fields, but in no way does it feel like a proper, language-supported sum type.
* Javascript semantics leak through to TS all the time. Which is kind of the point, TS is supposed to be a static typing layer on top of JS.
This is an advantage too: you stay close to the actual runtime and it's easy to interact with non-typescript code.
But a lot of stuff still feels quite messy, like a bolted on abstraction (which it is).
> * 'readonly' support, while nice to have, is mostly broken. It's just way too easy to coerce a readonly value into a non-readonly one, especially with nested objects.
Do you have any example of this? To be honest, as much as I'd love immutability support built into the language, the number of bugs I get from accidentally mutating data is so low it's not something I would make a lot of effort to get. There's always Object.freeze as well which will throw an error if you try to violate immutability.
> * Javascript semantics leak through to TS all the time. Which is kind of the point, TS is supposed to be a static typing layer on top of JS.
Does this not happen in BuckleScript/Reason as well at the layer where you interface with JavaScript libraries?
> But a lot of stuff still feels quite messy, like a bolted on abstraction (which it is).
Yeah...it's practical though and has an easy plus incremental migration path. Switching to OCaml would be much bumpier.
If you have some time, please give that intro page a read. Hopefully that intro page explains what we're achieving. If you'd like more nuanced opinions, we're always in Discord (discord.gg/reasonml).
1. I feel like I shouldn't feel ashamed when saying this, but here: mutation, imperative code & side-effect are kinda nice, and very much needed for a straightforward interop with e.g. existing JS/Obj-C/Java (hint hint) code. For example, notice how BS externals are erased & inlined. Most of our ffi doesn't have a conversion cost. An excellent ffi can be a deal maker, considering that if you're serious about migrating your codebase to a new language, you'd be using more ffi than idiomatic code for a while. Early on, I've had folks accidentally modify the output JS artifacts on messenger.com because they thought I wrote the JS code myself; they were generated by BS.
2. Also, speaking of native compilation: we're not reference counting but _some_ predictability when working with UI code is needed. The GC has to be fast and collect things within 16ms (and now, 8ms since iPad Pro is often 120fps). The ocaml gc achieves that.
Those aren't my findings: those are the findings of the creator of React, who also created Reason.
3. Finally, and to corroborate with what we wrote on that page: if you go ask your "normal JS colleagues", you'll realize most folks don't _actually_ know the semantics of JS. They like JS because it _looks_ familiar. Is TypeScript "just JavaScript"? Does it matter if JS folks can pick it up and be equally productive quickly? OCaml/BS semantics map over to JS well enough that we can just change the syntax into something familiar, and you'd likey end up writing the code you'd usually write in JS, albeit backed by a type system that needs to make no excuse in terms of quality.
Hope that helps? Tell me if that makes sense.
(Tail recursion has been a problem for a single person so far afaik; you do have imperative loops)
> I feel like I shouldn't feel ashamed when saying this, but here: mutation, imperative code & side-effect are kinda nice, and very much needed for a straightforward interop with e.g. existing JS/Obj-C/Java (hint hint) code.
Similar feelings...OCaml has mutability built in but you don't need it often. You can isolate its use to small areas of your code to keep it under control. If you went the Haskell route getting JS developers to adopt would be even harder.
> Also, speaking of native compilation: we're not reference counting but _some_ predictability when working with UI code is needed.
So the GC is well behaved with Reason scripts? I'm curious if you could use it for games.
Thanks for the insights. I haven't heard of Reason much to be honest which is a shame. I used OCaml as my main language for several years and going back to Java, Python and JavaScript was pretty painful!
Yeah. Some existing algorithms are more easily expressed with mutability, and for FFI it's very needed; I think some community-wide messaging regarding this is good enough. Constraining them at the type level might make adoption a bit harder.
We're just a syntax for OCaml; we don't change how it runs. Same GC. Reason's mMuch less sophisticated than you think, but also much fewer unknowns. https://reasonml.github.io/try/
The goal of Reason (and of BuckleScript) is so that people can convince their coworkers that OCaml isn't an esoteric unmaintainable language. BuckleScript is JavaScript: The Good Parts: The Good Parts.
TypeScript doesn't support deep readonly, which would be really helpful for Redux, where you're required to copy anything you change, but can easily forget.
When I tried Typescript briefly, it seemed like the compiler was surprisingly slow. Not super slow, just nothing at all like writing raw JS directly, or Python or anything like that.
This blog post emphasizes that BuckleScript compiles very quickly. Sounds very appealing.
> It also has tagged unions, product, and sum types, nullability, and extensive capabilities for typing 'this'. So I'm left to ask what exactly is the unique advantage of writing JS in OCaml? Because it's not immutability. Remember too, that OCaml has plenty of warts.
I have similar feelings to be honest. Working in TypeScript, I know I can easily Google for JavaScript snippets, libraries and issues when required but if I'm working in OCaml it would feel too isolating. In TypeScript, I'm already using immutability, non-null/undefined types, no implicit any types, modules, map/reduce/filter etc. I'm not sure OCaml has enough advantages to overcome the downsides of working in a niche framework right now. I do miss OCaml's terse syntax though.
IMHO TypeScript/Flow missing pieces are 1) runtime side of type system (match) and 2) expressions instead of statements. Those two things complete the type system reaping rewards from annotating code with types. Both TS and Flow have reservations on adding those constructs to the language as it requires runtime support (match) and would be seen as too far departure from js (changing statement -> expression semantics).
The unwritten motto of TS/Flow is "stripe type annotations and you've got vanilla js". Adding support for match and expressions would mean adding runtime dependency and transformations which would violate this 1-1 symmetry.
This is a shame because type annotations are the hard/boring/sacrifice part, runtime matching is easy/fun/benefit out of it and expressions are just... I don't have words... - why would you think of making any of those statements? It's just pure limitation, adding verbosity, without any reason.
A similar stack is Fable and F# with the Fable-Elmish library. It also works well with react/redux tooling (debugging in the browser supported), and has great tooling support in Visual Studio Code.
Clojurescript is nice a option too, you get a language with better defaults and you don't have to pull in immutablejs/lodash or you favourite immutable and util library and deal between all the inconsistency between them. All the ES6/7/8 stuff is there too plus other niceties.
> You don't have to worry about JS scoping rules or how `this` works, or how ES6 classes are ultimately prototype-based
I don't worry about these things. I feel like I'm writing a lot less JavaScript these days, mostly through the power of React, JSX, and ES6. I only really use Typescript to type DTOs. I just don't have enough problems to scrap everything for a completely new language.
> I just don't have enough problems to scrap everything for a completely new language.
I was not recommending that at all. I tried out Typed FP only after a project with a lot of complex data transformations (which is a sweet spot for Hindley Milner based types) and couldn't hold it all in my head. In the React world, the equivalent would be a deeply nested component hierarchy, passing around a lot of props, and having a large state-tree and correspondingly complex reducer.
You can definitely use the typical Redux+TS/Flow+Immutable stack there without any issues, but I think there is a much better developer ergonomics on offer here.
Instead of bolting on typing into Javascript (Flow and TS both do a good job of it btw), you could simply use a language that was designed with it. There is a huge difference in native adoption of such a core language feature vs using a transpiler that ultimately leak Javascript semantics.
If you're using immutable values through ImmutableJS-like libraries, or by just being careful to not mutate stuff - then you should try working in a language that has immutability baked in. Having confidence that you've been careful to not mutate anything in your code vs using a language that simply disallows it in the normal course of programming brings a tangible difference.
You can call BuckleScript/Reason as just a better version of Javascript with all the good things baked in. But it definitely is much more - you get to use Variants (Sum/Union types in other parlance), and more importantly, writing value-based immutable code (instead of reference based mutation) lets you rely on equational reasoning to understand your code. Your functions are pure and can be seen as though they are equations, and your whole program is a composition of these equations.
A good thing about BuckleScript/Reason vs transpiled languages like CoffeeScript is that we're fully free from Javascript's semantics. You are solidly inside OCaml, and Javascript simply doesn't leak through. You don't have to worry about JS scoping rules or how `this` works, or how ES6 classes are ultimately prototype-based. Except for a well-defined foreign-function interface that lets you use all of npm libraries or your existing JS code, once you are in the typed immutable world of OCaml, then that's all you need to think about.
All of this applies not just to BuckleScript, but any Typed FP that runs on the browser - Elm, PureScript, and I believe Scala.js as well.
But one big advantage (or disadvantage, depending upon how you look at it) of OCaml/Reason/BuckleScript is that it allows mutation if you need it. You can start out writing purely imperative code just as you would in Javascript, and slowly adopt immutable programming as you get comfortable with it. You can also rely on mutation when you need to optimize specific parts of your code.