ReasonML versus BuckleScript

You have to specify them if they are carried from a higher scope. The ‘call’ point does not need to be specified, which OCaml will match in succinctness when the Implicit Modules PR lands (and even right now it’s only a single extra token, so big whoop).

And yes I’m assuming putting in type declarations, Haskell definitely follows the pattern of declaring the types on every declaration because it’s inferring process can be crazy slow otherwise in complex code. :wink:

Oooo, interesting, quick reading…

It looks like SML’s modules, simple polymorphic types, no higher polymorphic types (which is what OCaml’s supports). So it is basically just a genericable module system, no HPT’s still.

OCaml is BIG on purity, I’m not sure why you would think otherwise? OCaml does have escapes out of it though, it basically follows how Erlang/Elixir do it, use purity everywhere possible, but be able to break out of it easily (like to write to a file) to Get Real Work Done without writing pages of extra code. Both OCaml and Erlang were birthed from a place of needing to do Real Work, and the languages reflect that, where Haskell was birthed as an interesting research project in comparison so it is interesting to use but not near as practical to get work done as OCaml or Erlang or so. ^.^;

And the typing of operators sucks sure, but you can easily redefine them, or when Implicit Modules comes out then you can just make implicit +'s that work on any types that have modules defined for the functions of that type. :slight_smile:

Though currently it is nice that you know what type +. is by default until you overwrite it, which is always visible in the scope. :slight_smile:

Well in OCaml:

type myVariant =
| Thing1
| Thing2 of int

type myStruct = {
  something : int;
  blah : myVariant;
  more : string
} [@@deriving yojson]

let thisIsAStringOfJson =
  myStruct_to_yojson {
    something = 42;
    blah = Thing2 16;
    more = "Hello JSON!";
  }

let Ok thisIsTheStructBack =
  myStruct_of_yojson thisIsAStringOfJson

And you can load a PPX like Yojson into bucklescript, it’s not a drop-in usage like NPM packages are there, but you can always use the OCaml calls directly or an OCaml build system instead.

Plus Bucklescript is not the only OCaml → Javascript engine, js_of_ocaml is an older one, though it’s output javascript code tends to be more unreadable, however it can easily convert about any OCaml code without always needing the source (I don’t think it ever needs the source actually, it converts from the bytecode step, not the AST, which is why it is more unreadable but more powerful), where Bucklescript always needs the source.

I LOVE how fast OCaml compiles, even faster than the dynamic languages of Elixir/Erlang. ^.^

Very fast code at that. :slight_smile:

FYI:

TL;DR: Higher Order Components (HoC), introduced into React to address the shortcomings of mixins aren’t a particularly good fit with ReasonML as it is statically typed (pain point: passthrough props). Turns out another solution already appeared in ReactJS that is a good fit with ReasonML (which uses functors).

Mixins were a tactic to share code that had its problems:

Once React moved towards using ES2015 classes higher order components became the standard way of sharing functionality. Higher order components have their own issues as outlined here:

See also Render callback and Function as children.

The “render prop” approach is used in: Advanced ReasonReact: Higher Order Components

… “how do I spread props in reason?” And the basic answer is “you can’t.”

Named props translate directly to named arguments on the component module’s make function, and there’s no support in OCaml for “I have a bag of arguments please dump them into this function call”.

One response here is “can we change ReasonReact’s API to something more spreadable?” Even if we did, though, there’s an issue of getting the types right.

The interesting thing is that the solution uses functors (aka parameterized modules or module functions).

An OCaml functor is essentially a module that acts as a function for the purpose of returning an augmented instance of an input module (that matches the specified module signature).

1 Like

2017-12-01: new BuckleScript website

Seems the ReasonML team spruced up the BuckleScript documentation - hence the similar look and feel.

4 Likes

2017-12-13:

redex - REason package inDEX

T minus inf days

1 Like

Just need someone to finish it out. :wink:

So, I decided to make the rounds in “compile to JS” land again yesterday, with the usual stops mapped out. Oh, boy, did I miss something with regards to Reason syntax. They added a bunch of useless notation to unsolve a solved problem. I’m totally with @jeremyjh on this one; they made using the language worse for the people who are most likely to use it in order to appease people who are still extremely unlikely to use it, given all the massive work that’s been done to provide much more palatable JS alternatives that solve the most obvious issues.

I’m usually of the opinion that syntax doesn’t matter much, but that’s always working under the assumption that you don’t make it objectively worse (more ceremony, not for syntactical precision or ease of parsing) for no other reason than community appeal.

2 Likes

Heh, that really does describe it well. ^.^;

I’m of the opinion that syntax does matter for productivity. It does not matter for how ‘well’ you code, but it absolutely matters in both writing and reading comprehension and speed (this is why I find Elixir needlessly verbose and slower to read/write than even erlang).

ReasonReact 0.3.1:

  • Router! It’s tiny and has almost no learning overhead.
3 Likes

I just had to delve into ReasonReact as well. I thought I was going to seriously dislike the syntax but their “version 3” (which adds parenthesis and commas in function calls and definitions (oh the horror!)) is quite consistent and OK to work with (although quite verbose with unnecessary sugar, not unlike how I feel about erlang/elixir).

If it actually takes off I could work in it (it is ocaml after all). The more problematic thing I found though is that they use too many imperative constructs and make use of the “escape hatches” ocaml gives you without giving it a second thought. Lurking a bit on their discord channels it seems they are not afraid of doing side-effects from where-ever (I guess the rational is to fit into React).

Even ReasonReact choose to use mutable Arrays rather than Lists for things I’d always use a List for, just because it makes it easier to interface with javascript.

If the language is used this way I don’t think it will buy them much over TypeScript for example. I think immutability is the most important aspect of writing robust code. Strong typing like Ocaml is really nice to work with but if you use mutable constructs and side-effects in too many places it is not going to be easier to reason about and you’ll have logic bugs regardless of how strong your typing is.

With this in mind I don’t think (unfortunately) the ReasonML language will improve the Ocaml eco-system or have much of an impact.

It is likely that a big share of the javascript developers new to ReasonML will write “imperative” code in a functional language. It is a shame because it is a functional language with a big company backing it so it may actually have had a decent chance of becoming something.

So I am torn on what to use.

  • Elm - Is really nice. Two things gives me doubts. Currently no way to to higher abstractions. and The language is moving way to slow, you don’t know what will end up in the next release or when the next release is due, how backwards compatible it will be, what core libraries have been rewritten, etc. That is why I feel quite unsure of starting anything big in it
  • ReasonReact - I haven’t really used react but it is used everywhere. ReasonReact slots nicely into the existing react world but gives you (in theory) a good underlying language. My problem with ReasonML is outlined above
  • BuckleScript - Writing OCaml code with ReasonReact looks like it is trying to push a square peg into a round hole. The other alternative is the bucklescript-tea which I’ve played with a little bit. My concern here is that it doesn’t have any mind share and small community.

I don’t think any other compile/transpile-to-javascript languages is worth the trade-off.

So.:

  • Add a Generics construct and proper project management to Elm and I use it
  • Move ReasonReact towards immutability and a functional mind-set and I’ll use it
  • Grow bucklescript-tea and I’ll use it
2 Likes

That’s my library! ^.^

I use it actively, as do a few others. I am very intent on not breaking backward compatibility for note. New features will extend, not break. Breaking will only occur if bucklescript itself does something stupid (it’s broken it’s API a couple times, though it’s more stable as of late) or if there is a really major bug or performance issue that needs fixing (I don’t think so, it even benchmarks faster than Elm, which is already one of the fastest out!). :slight_smile:

I accept PR’s! I’ve kept the core fairly small just because that is the hard to build part, almost everything else above this is trivial in comparison and I usually just build it in-place in my projects, but PR’s for new features is always welcome and as long as it’s well made and belongs I’ll not turn it down. :slight_smile:

3 Likes

That is cool. Ok, so I might give bucklescript-tea ago again :slight_smile:

Currently you are basing it on Elm 0.18. What is the plan with Elm 0.19? Do you plan to keep following Elm or diverge based on 0.18? I guess there are pros and cons with each.

1 Like

I’m not an expert on this but I feel that React doesn’t really fit the functional and immutable style of Reason. React seems to need javascript to shine and be easy to grasp, could be just a feeling for me though. But I feel my code is more beautiful and easy to grasp in Elm rather than in ReactReason. But that could be because Elm was designed from the beginning to be a front end language.

The model that seems to work well though with the functional style in general is TEA. As I said, mentally and syntactically it looks far more clearer to me than ReactReason. That’s why I think if more of the community of Reason started realizing bucklescript-tea it could have a nice community around it that’s similar to Elm’s community.

Thanks @OvermindDL1 for the library, I didn’t use it yet but I’ll definitely try it one day

2 Likes

Yeah, I’ve noticed that needing to use ReasonReact.arrayToElement comes up with annoying regularity (usually requiring a helper function with Array.of_list). But as you say it has something to do with interop with React. But all things considered I think that ReasonReact in some areas actually cleans up React’s API (e.g. reducerComponent) - though it’s far from perfect (seriously, people need convincing that Function as Child Components is a bad idea? (… and that it’s somehow “the same” as a render prop?)).

If the language is used this way I don’t think it will buy them much over TypeScript for example. I think immutability is the most important aspect of writing robust code.

At least in ReasonML/BuckleScript you can often choose to do things the “right way” - though %raw is usually a red flag; I guess it’s useful to get things to work in the short term but it really needs to be replaced with proper FFI/decoding/encoding in the long run. TypeScript doesn’t have immutability by default, the type system isn’t sound and being a superset of ECMAScript it’s in danger of having syntax conflicts with future ECMA specs.

you’ll have logic bugs regardless of how strong your typing is.

And unless they rewrite React in proper BuckleScript/ReasonML (not gonna happen) your code is still operating imperative machinery.

a functional language with a big company backing it so it may actually have had a decent chance of becoming something.

The issue at this point is that ReasonML is joined at the hip to React. It’s a benefit in the short term as React is pretty well established giving ReasonML greater exposure. In the long term React’s inevitable decline will drag ReasonML down with it unless it somehow manages to tread it’s own path.

(For all we know the next view framework will find a more efficient way to identify updatable components that doesn’t rely on a VDOM and doesn’t require you to optimize something like shouldUpdate.)

Meanwhile ReasonML seems to be stealing all of BuckleScript’s thunder - which I see as a good alternative to PureScript.

My concern with TEA is it’s monolithic state. “Single source of truth” sounds all nice and dandy but these tools are usually only justified for complex (and usually large) UIs to begin with. So there may sometimes actually be an argument for having a single source of truth for any particular thing.

Move ReasonReact towards immutability and a functional mind-set and I’ll use it

This actually seems to be happening to some degree right now - again reducerComponent is far more disciplined than the equivalent JavaScript component that it is using under the hood. The question is whether they will compromise things further to make onboarding for JavaScript developers easier.

I guess it’s too much to hope for that implementing ReasonReact will make them realize where they have gone off the rails with the React API and motivate them to rein things back in.

2 Likes

Thanks for your great reply (as usual, I enjoy your posts. They are well researched and comes with lots of good insights!).

I am not too familiar with the architecture yet. I’ve seen though that the main update function gets pretty large. Looking Richard’s elm-spa-example (GitHub - rtfeldman/elm-spa-example: A Single Page Application written in Elm), even this really small SPA the main function is pretty large and lots of different states that you have to keep track of. He also is forced to do a “catch-all” pattern matching (_,_) in one specific place which basically throws out all the help you get from the compiler.

i am hoping that bucklescript-tea sort of can help abstracting these things away a little bit.

I think I would like a mix between TEA and a message-passing based architecture. Where different components (which is an overloaded word for sure) can send messages to each other and ideally have a statically checked contract between them. A user interface built on BEAM principles which I think would work really well. I don’t know if it is practically doable in javascript though and I have so little time :confused:

I am just so cautious about picking something javascript, as I’ve been burned by the constant churn before (with angular 1, bower, grunt and what not). That is actually why I sort of like elm as well because it comes with tools decoupled from the javascript normal world.

So, once I announce what I am going to use you better pick something else unless you want to make the wrong choice too :smiley:

2 Likes

Sounds all too familiar :grin:. Initially I liked Angular 1 - until I realized that it actually was some kind of OO leviathan that somehow became more about itself than the web frontends you were trying to create with it.

Quick peek at ClojureScript but at the time the frequent sprinklings of JS interop code (should I be using vanilla JS instead?) were a bit disconcerting (that may have changed by now).

Looked into Elm, again liked it, but I think the community growth is going to taper off now so I’m concerned that there won’t be much further growth in adoption.

Looked into React, preferred Elm.

Looked into Vue - it’s reactivity system embraces mutability. Though it’s a likely destination for Angular 1 refugees looking for a leaner tool and React devs who find the React ecosystem just too much to deal with.

Only circled back to React because of ReasonML (and because React skills may have broader applicability).

2 Likes

Might happen if Jordan and Cheng managed to convince the team… But yeah I wouldn’t hold my breath.

Even so, the type system alone is a huge improvement from vanilla JS. I’d pick HM-type-safe imperative code over untyped one. Besides, JS community also have a considerable number of FP enthusiasts that avoid class like a plague (except when using libraries, like React.Component), so I don’t think it will be too bad.

2 Likes

I assume you’re talking about this:

It’s not especially large and in (several?) talks Feldman actually states that function and module length isn’t as big of a problem as you might think, because it’s not Javascript, so his position on this is pretty clear. As to your assertion that there are lots of different states to keep track of, I don’t see how you need to keep track of them at all. Not even knowing the rest of the source code, the update function reads as a specification of what needs to happen on certain events, pretty clearly specified.

You can bubble up and send down messages in Elm as much as you want. The following illustrates what is an analog

        ( LoginMsg subMsg, Login subModel ) ->
            let
                ( ( pageModel, cmd ), msgFromPage ) =
                    Login.update subMsg subModel

                newModel =
                    case msgFromPage of
                        Login.NoOp ->
                            model

                        Login.SetUser user ->
                            { model | session = { user = Just user } }
            in
            { newModel | pageState = Loaded (Login pageModel) }
=> Cmd.map LoginMsg cmd

We bubble down the LoginMsg we received down to the Login component that then runs its update function and returns ( ( pageModel, cmd ), msgFromPage ) and we case on msgFromPage to determine how our top-level model is to be changed.

I haven’t looked deeply enough at this, but I’m doubtful that he’s “forced to” do this.

Elm has some problems, but I really don’t think any of them have to do with state management and components. Actually, I think the reason people end up with massive model records (100+ fields) is entirely reasonable. It’s been basically proven in at least two eco-systems (Elm being one of them) that top-level atomic state blobs/stores are simply better and simpler (and in the case of ClojureScript with Om.Next also allow you to map the state tree to a query language that can automatically batch requests and the queries are directly related to what each sub-component says they need, allowing you to mold the queries automatically to the actual view requirements of each component, bubbling this specification up to the top-level that can then query for each sub-component as is required).

In languages/frameworks where that store could be changed from anywhere at any point the concept is terrifying, but with a clear route that has to be followed to change that blob it isn’t even problematic anymore, it’s just good.

Elm’s only issue (and also its upside if you’re a “Javascript programmer”, I’d bet) is that you don’t have access to the nicer type abstractions so once you get hooked on types you can’t use Elm to progress into the world of types, so you have to go to Haskell/Purescript/OCaml, etc…

2 Likes

I think his point of view is that the compiler will help you and therefore it is not as dangerous as javascript. I think his example has 6 pages or so, which is a minimal SPA. My phoenix application has around ~150 routes and it is what I feel of normal size for a web-app.

Forced is likely a bad choice of word for me. Just that he has modelled his updateDate page so that it can have “impossible states”. He has another talks where he talks about the importance of avoiding this. I.e model your types so that impossible states become impossible. They also praise the type Variants where the compiler will warn you if you don’t handle a specific type. In the updatePage function the compiler wont say anything if I forget to handle a PageLoaded somewhere, thanks to the catch-all clause. If you have 150 routes you are bound to miss one or two. Is this better than javascript? Yes, likely. Is it good enough? I don’t think so.

I don’t know if this can be changed somehow but I feel that having some sort of generic structure could change this so that there only would be a few clauses to handle i.e it screams out to be able to do something iike this:

case (msg, page) of

    ( PageLoaded page (Ok subModel), _) ->
       { model | pageState = Loaded (page subModel) } => Cmd.none

    (PageMsg subMsg, Page page subModel) ->
       let pageModel = page.update subMsg subModel in
       let newModel = something in
       { newModel | pageState = Loaded (Page page pageModel) } => Cmd.map PageMsg cmd

I am hoping the above can be done in bucklescript-tea, through some sort of language construct.

Elm is supposedly written to be easy for beginners, but I struggle quite a bit with it parts of it. Their standard one page ‘Tea’ is easy but hooking everything together is where I struggle. I can’t see the structure through all the boiler-plate. I don’t care about the implementation I just need to see what is happening and the lengthy non Dry case clauses doesn’t give me overview.

So, in conclusion you are not making my decision any easier. :smiley:

  • @gon782 gives good reasons why some of my worries about Elm is not full warranted
  • @OvermindDL1 tells me that bucklescript-tea is more active than I think and a good choice
  • @peerreynders states some advantages for using ReasonReact.

I guess I’ll just keep bouncing between the three frameworks on my Proof-Of-Concepts :smiley:

  • Stick with Elm and continue until you hit too many pain points - which may never happen.
  • If it does happen you have to evaluate whether TEA got into your way then ReasonReact is your second choice.
  • If TEA stayed out of your way but you found the language too restrictive just move over to BuckleScript-TEA.

Now you could argue that BuckleScript-TEA is more in a “T” position allowing you to jump either way but I think in the short term Elm is more convenient. Just be aware that Elm is the biggest PITA for JavaScript interop - it’s unlikely to lead to no-JavaScript nirvana unless you can keep everything self-contained.

3 Likes