ReasonML – React as first intended
imaginarycloud.com
imaginarycloud.com
From my point of view, React solved a real problem. JS before React (at least old-skool, with jQuery & co.) was simply unmaintainable. It was often easier to thow old projects away and start anew than to maintain what inevitably became a tangled mess of callbacks and global variables. Not because of incompetency of developers (at least I hope so ;) but because there were no standard of encapsulation, separation of concerns and similar.
Why would someone who knows React want to switch to ReasonML?
On a large code base, increased maintainability can be a huge commercial advantage.
The current problem with ClojureScript interop is the Google Closure compiler's support for stuff on npm. I switched our team to using shadow-cljs[0] and it solved all of those problems. Would highly recommend.
Using JS from CLJS is incredibly easy. Like I said, shadow-cljs solved all of our problems there.
Going the other way - using code written in CLJS in JS - is another matter. This is not well supported by the ecosystem. shadow-cljs has much, much better support for this than the standard ClojureScript/closure compiler, but it doesn't hold a candle to what BuckleScript does.
IME, doing a top-down transition - where you convert the top of your app to ClojureScript, that calls everything - works best. From the top, you work your way down into each feature.
ReasonML, on the other hand, works better from the bottom up. Typing your whole app is hard, so you start with some small feature that then you export to JS land (since that's so much easier with BuckleScript). From the bottom, you work your way into writing more and more features in ReasonML.
1. Immutable by default (with sane escape hatches) 2. Fully qualified namespaces 3. Functional (with sane escape hatches) 4. Good interop with host platform
At the beginning of this year, my team started a green field project to replace our old system. I had the chance to steer us toward ReasonML or Clojure(Script), and we decided on Clojure.
The main reasons were:
1. The number and maturity of libraries for Clojure(Script) was much higher 2. Clojure's server-side story was much better, and we needed to write a ton of services as well as client-side code 3. The ecosystem, language & tooling was much more stable for Clojure back then
I think that ReasonML has a ton of great ideas, it just lacks maturity. I love the language itself, but I would not yet want to inflict it upon my team mates who have spent the last 10 years writing Java back-end services. Clojure(Script) ended up being a much better fit and I feel like I am just as productive, if not moreso, than when I was writing code in ReasonML.
Elm provides type safety, immutable data structures, and state management in a very compact but readable way. In fact, Redux was strongly inspired by Elm's state management.
All of these stacks are perfectly acceptable choices, so it comes down to a matter of personal preference. I'd encourage everyone to spend some time with Elm and ReasonML. I find them to be productive and very enjoyable tools.
PureScript deserves a mention in this list as well. More full-featured and general purpose than Elm, cleaner design than ReasonML. Prolly the smallest community of the 3.
I'd advise starting with Elm to fall in love with the development experience and wonderful error messages, and graduating to Purescript when you need more power.
With Elm the situation is very different. All your code is pure. The runtime hides the state management and the only way to interact with JS is via message passing. The end result is code that is extremely easy to follow, maintain and debug. Moreover, as Elm style and tooling discourages using lambda-style callbacks with opaque state, all state is very explicit and often just by looking at the state data structures, one often can grasp what the code is doing. It is almost like code follows from the data structures.
PS is a lang. It compiles well to JS, but there is no friction against making it compile to native, apart from the time it takes to maintain that compiler backend.
On top of PS you may pick a FW. Some have React.js under the hood, some follow more closely The Elm Architecture.
Only with a FW, PS can be compared to Elm.
And yes, Elm is opinionated. But that is a consequence of being very simple and pure functional language. As its typesystem does not support using monads or effects to model state with code similar in style to that in non-functional languages, one inevitably ends up with a runtime similar to Elm browser bindings in any Elm embedding.
Essentially Elm is opinionated because its pure functional style is the only style supported by the language with no escape hatches like the do blocks in Haskell or PureScript.
Here is the official React binding (or whatever you call it) for ReasonML: https://reasonml.github.io/reason-react/
Using this you would still be using React, just in a different language. But my understanding is that you would not be using redux (if you are now) because it doesn't make sense in a language like ReasonML.
Why would using Redux not make sense? It's not like the problem of sharing state between components goes away with a functional language?
"When using ReasonML, we don't need Redux anymore. ReactReason stateless components already come with the concept of a build in reducer, which is meant to take care of the problems Redux used to address."
this.props.dispatch({type : "INCREMENT"});
Nothing's ever _forced_ you to use action creators, or declare your type constants separately.However, we encourage use of action creators as a good practice. See my blog post "Idiomatic Redux: Why Use Action Creators?" [0] for some reasons, as well as the related Redux FAQ entry [1].
[0] http://blog.isquaredsoftware.com/2016/10/idiomatic-redux-why...
[1] https://redux.js.org/faq/code-structure#why-should-i-use-act...
> 1 ... put the logic for creating that action in one place
This isn't so simple. You end up duping the action in at minimum 1 AC (often many ACs), and onboard the complexity of an unnecessary function, plus importing and connecting that function all over your app. A UNIQUE_ACTION_CONSTANT is an easy refactor, unlike action creators, which can cause a cascade of changes when reworking component structures.
> 3 ... larger logic that goes into preparing the action object,
This justies a function, not necessarily an AC.
> 4 ... lets the action creator worry about how to handle things
Smart components are already aware of the state system by means of connect. Further, in AC aware apps, you have a whole swath of extra imports to manage, more connect calls to manage, and a binding API to consider, which sometimes requires patterns and team agreement on their own. dispatch + messages does away with all of that, and is shown to be simple/effective as popularized in Elm and Reason.
> 5 ... testing
Kinda. I'll meet ya half way here. But stubbing disptach and/or component methods is also very cheap, and super simple to reason about.
I say this all in the spirit of collaboration and productive debate :)
- Frontend: Your tools are fast. The ReasonML-to-JS compiler is already an order of magnitude faster that TypeScript, etc. Fastpack is a native bundler in the works that aims to bundle your project in under a second (and faster for incremental builds).
- Mobile & Desktop: Some are experimenting right now with writing a version of React in pure Reason that compiles to native code, bypassing the JavaScript runtime entirely.
- Backend: You'll be able to use the Node.js ecosystem but compile certain modules to native code when you need critical performance.
Edit: I should add, the Node.js backend story is currently the least developed part of the ecosystem (I'm personally working on getting that up to speed).
1. The compiler optimizations being applied to code - Things like GADT's are able to be allocated very efficiently - Can generate code that doesn't box at runtime (smash it into a pointer is done as well) - Pointers are word-aligned
2. The OCaml runtime - No JIT - No warmup - Can emit native code
3. The HM type system - Typecheckers for HM are really simple [1]
[1] https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_sy...
This simplicity + the features of the compiler combine to make the OCaml compiler very fast, and very easy to write one (some undergrad CS classes do), and developer-time tenable to make both happen.
https://reasonml.github.io/blog/2017/09/08/messenger-50-reas...
> Honest question: why would someone use this instead of React?
The libraries I mentioned have __a_lot__ of stars on github, I'm sure many people using them, those might be better off with ReasonML.
I hope ReasonML will succeed and the ecosystem catches up. ReasonML is the fucntional general purpose language I always wanted. If they get the concurrency story right it might become a nice functional alternative to Golang as well.
Why would someone who knows Dojo or GWT want to switch to React?
I think this article's conclusion of Reason being the enterprise grade and mainstream implementation of Elm, and Elm being a "prototype" is a little unfair to Elm.
Elm has developed a mature ecosystem in many ways, in a relatively short space of time, and two of the main reasons for that are the purity and the way the JS interop works. It explicitly shields the Elm world from issues in JS. This is one of the reasons we've decided to choose it. It is much more than a prototype, this was something that factored into our decision making process heavily.
I'm sure there are great things about Reason, but I can't help but feel that the impact will be limited because it doesn't take a stronger viewpoint.
I would also love option to have gradual type friendliness because when prototyping I know my program is wrong in some different part, but I want to test given component in runtime, while I haven't refactored everything yet. I think having option like 'compileWithoutTypecheck' is very good for prototyping.
http://swannodette.github.io/2013/12/17/the-future-of-javasc...
Interested to take this for a spin.
His take on React in ClojureScript would probably be a better qualifier, there have been others. Reagent is a popular alternative take on a cljs React interface.
Once you've handled local state and remote data dependencies, you have like 10% of your app state left. That can easily be solved with a global atom (a la re-frame) or something like the new React context API.
Our understanding of how to handle state, remote data fetching, etc. has grown a lot over the last few years. Both the React & ClojureScript ecosystem went all-in on global state stores like Redux because, at the time, we thought we needed it. It felt like, if we needed a global state store for one thing, that it made more sense to just put everything in there for cohesiveness. It also was hard to determine up-front what was needed globally (mostly data IME) and what was only needed locally.
It turns out that local state and something like apollo/relay/"suspense" for remote data solve the 80-90% case, and now we're getting better tools like React context to handle the last 10%. I don't think that Redux/re-frame is going away, but the number of apps that I would implement with them has gone way down over the years.
How much is it easy to use JS components like the Calendar of Airbnb for instance? And is it really easy to use BuckleScript? The "[%bs.raw ..." and others look hard to read/use. Is it the case or just a feeling?
Is there someone who already tried it enough to know the answers?
Highly recommend giving ReasonML a try.
When you're outside the FFI, the language really shines in build speed and output. Support of ES Modules out of the box is nice too.
Disclaimer: I've only used it for small Todo project, I'm not a front end guy.
Disdisclaimer: I've not used React at all.
However, if you need really strong JS interop, consider something like F#'s Fable. F# is gaining a lot of momentum and now that SAFE stack is apparently Microsoft-blessed, I expect it to continue growing even more.
Suave though... Nothing against Suave, but it's not the Phoenix competitor the ecosystem is in dire need of. I was under the impression some newer frameworks like Giraffe were going to fill that void..
* "Official" Cross platform support -> dotnet core FTW!
* Cross-IDE intellisense support -> Ionide, Check!
* Dotnet core support -> Check, finally(and make sure to set Ionide to use core)!
* F# to javascript cross compilation project with some traction -> Fable, check(Fable needs serious documentation help though)!
* A great framework competing head on with Phoenix -> Hopefully soon :)
The potential just seems sooo massive. Hopefully it blows up quicker than Haskell ;)
Rider also works pretty well with F#, which is nice if you prefer the JetBrains IDEs.
?
It's what the S in safe is actually meant to be, not suave. It's very similar, in heart anyways, to phoenix.
The fsharp community is fighting what is essentially an uphill battle. We have ML but we've got to jump through dotnet hoops to get shit done.
One man's cruft is another man's feature.
I doubt ionide will support SAFE, but maybe they will have a sister plugin? Idk how all that works.
Ionide could just use some extra help I mean; it appears to be the work of just one guy. Hopefully with the increased interested in F# web development it will get some extra love more ala omnisharp.
Things like limitations on statically resolved type parameters being supported only for inline functions, etc. don't make sense for a language targeting javascript.
It is interesting to note that F# took a lot of concepts from OCaml but stripped out functors, structural typing support etc. to adapt to dotnet. These adaptations are largely required because Microsoft does not treat F# as a first class citizen (at the same level as C#) and CLR is very explicitly geared towards object oriented languages. An occasional endorsement from Microsoft does not magically obliviate these deeply ingrained design decisions in F#.
When targeting javascript these adaptions become really pointless. As demonstrated by Microsoft's own TypeScript language, Structural typing can be quite useful for interop.
It would be really nice if F# was a first class citizen in dotnet world and javascript was a first class compilation target maintained by the F# core team adapting the language to javascript target in the same way it currently adapts to dotnet. However that is not the present day reality. However typescript, despite being a javascript superset (and inheriting all its warts) has a really flexible type system and lends itself well to functional programming (as demonstrated by Giulio Canti's fp-ts [1] project).
Unlike ReasonML, however, typescript's type system is not sound. I have written a more detailed comparision [2] of TS and ReasonML. I do actively use TypeScript for my day job and mostly love it, but I am much more optimistic about ReasonML once the ecosystem matures.
tl;dr: The 80% best practices parity between ReasonML and modern functional javascript which the "What & Why" page of official documentation [3] talks about, does not exactly hold true for F#. So, while it is not that F# is not a great language, or that you can't build great apps with F#, however, as it currently stands, I don't see any compelling advantages of preferring F# over ReasonML, unless your backend is also F# and you want to (and can) share code between frontend and backend.
My familiarity with both F# and ReasonML is quite early stage (a few weeks each) and so would welcome any corrections.
---
[1] https://github.com/gcanti/fp-ts
[2] https://lorefnon.tech/2018/05/13/reasonml-vs-typescript-firs...
Also, to just jump in what your comment was actually about, I think the prospect of writing f# is generally a more friendly experience for those using it for the web. Or especially those using azure. Getting started with f# with ionide and vscode is literally the most pleasant onboarding I've experienced since ruby. It just goes! Amazing.
But yeah, I think reason is great too. I really like that ocaml has a centralized, external package management story. Using paket+fake isn't like a bad experience, and I mean, I wrote scala for a couple years, I can always find gratitude for a build tool of its NOT sbt, but it's awkward at times and feels like a bit more work than it should be. Of course from Microsoft's perspective, being able to jump around dotnet libs is a major boon.
You do web development primarily? I've been eyeballing TS lately, you think that's probsbly a better choice for more professional work until reasonml gets bigger pants to wear?
Pfft. Run reasonml/ocaml on node, one event loop pr core, and use message passing for synchronisation / distributing work ;-)
I'm only half-joking.
I do think that upcoming multicore support in OCaml is very exciting. I am more optimistic that it will attract people from Golang and C++. F# developers coming from outside dotnet is just too small a cohort.
> Getting started with f# with ionide and vscode is literally the most pleasant onboarding I've experienced since ruby.
Yeah, Ionide is really amazing. However merlin has also worked pretty well for me so far, and the emacs integration is a big plus for me. YMMV.
Also my comment was more around frontend use cases (compile to JS). I wouldn't be surprised if F# shines wrt Azure integration and within the dotnet ecosystem.
> You do web development primarily? I've been eyeballing TS lately, you think that's probsbly a better choice for more professional work until reasonml gets bigger pants to wear?
Yes, mostly node and frontend. In my area, professional work in reasonml is almost non-existent, but I do expect this to change in future.
As an developer coming from Ruby myself, glad to see more people from dynamic languages finding FP and type systems interesting.
Anyways, thanks!
The bulk of F# is functions acting on values modeled by types. This maps directly to JS. In the F# and .NET compatibility document[0], you'll note that all "core F#" components map directly to JS. And of course, you can consume JS libraries, TS definition files, etc. For example:
"The following F# semantic and syntactic features are also available:
* Records and Unions
* Structural Equality/Comparison
* Comprehensions (seq, array, list)
* Computation Expressions
* Pattern Matching
* Active Patterns
* Object Expressions
* Units of measure"
Do you have specific examples where F# semantics cannot map to JS such that the "80% rule" also does not hold?
[0]: fable.io/docs/compatibility.html
> Do you have specific examples where F# semantics cannot map to JS such that the "80% rule" also does not hold?
Yes, my comment was around some of the C# compatibility things, which don't make sense when compiling to other targets.
> Tuples vs struct tuples distinction.
> Having both modules and namespaces
> Explicit interfaces
> The strange coupling of SRTP and inlining
I find OCaml's support for structural typing in classes and polymorphism to be more flexible than the above.
In typed FP, runtime reflection is seldom used, and I believe reflection support (and associated overhead) should have to be explicitly opted in.
I also faced weird issues when tracing the source of an exception in an async workflow, and some incomprehensible errors around automatic generalization. But this was a long time back and I no longer have the full context. These may have since been addressed.
My intent was certainly not to criticise F#. It has been developed and is used by people far smarter than me. It is just that after a preliminary evaluation I have not found a strong reason to prefer Fable over bucklescript in the absence of a more deeper commitment to dotnet stack.
I am also really fond of many modern JS features like Module <-> Filepath 1:1 correlation, explicit imports, ES6 proxies, tagged template literals (placeholders etc.) which TypeScript elegantly inherits from JS. Support for intersection types in typescrpt is also very handy.
There is definitely a learning curve to JS bindings, but it pays off as you only have to learn them once.
Once I got over that hump, it was all fairly simple.
https://github.com/OvermindDL1/bucklescript-tea
Here some demo code (in ReasonML, not OCaml):
https://github.com/feluxe/bs-tea-starter-kit/blob/master/src...
ReasonML has a lot that makes it attractive to people that aren't already entirely invested in the JS/Node ecosystem, but doing the whole dance to make sure Node is installed correctly, and then using npm or yarn to install bucklescript and all the dependencies and make sure paths are correct, etc is a PITA, and a lot to ask of someone that's trying to evaluate whether it's worth incorporating in a new/existing project.
Provide a precompiled download with Node, npm/yarn, etc, and all the modules you need preinstalled, (just set the correct env vars to tell it to use it, such at PATH and some lib path) and you drastically reduce the overhead to not just trying it out, but actually using it.
By the time I was done installing ReasonML to my machine, I was done trying Reason - ML and OCaml seem great, but I have limited mental resources and they are over-invested elsewhere.
This serves to inform future technology trials, because we don't assign enough nearly credence to the cost of trialling new things when we are already operating at our mental limits. By the time I am in a sufficiently quiet spot to try your thing, I'm probably exhausted after a long day of work. So make it as self-exemplary as possible. Steve Krug nailed it in "Don't Make Me Think." (https://www.sensible.com/dmmt.html)
I.e. What is the correct number of buttons to start/stop your microwave: one or two? The answer is zero: your food should already be warm.
Since it compiles to JS, and uses a JS lib and ecosystem to install the utilities that do the cross-compile, it's sort of like if to try out Python you had to first download and install a C compiler and C libraries used in Python, and compile python from scratch, just to try it out, because no pre-existing binaries, packages or installers existed. If that was the only way to try out Python (or Ruby, or Perl, or anything), you would get a lot less people trying it out, and a lot less willing to go through all that in their build chain (in case python updates, right?) if easier alternatives exist.
Just because your language compiles to another language, that doesn't mean to you get to ignore the on-boarding experience to that extent. At least not if you really want to succeed.
If you're looking to build a web app with react (the subject of the article) then you can whip one up using one of their generators https://reasonml.github.io/reason-react/docs/en/installation...
I think either I wasn't clear, or you completely misunderstood me. My point, that Reason should have a standalone installer or package that gives you a working Reason as simply as possible without a whole build infrastructure required, is only bolstered by this statement.
I don't think it matters if it's the Reason-to-Ocaml targeted, or Reason-to-Javascript, or one for each, or one that includes both. Having to install one language, and then use tooling and package managing from that language to install another, strikes me as somewhat ridiculous in this day and age. just ship the entire tooling stack abstracted away.
And locally is important, since they might want to try a simple app with it, and not want to set up a whole Javascript dev toolchain with webpack. Sure, they might end up going that way eventually, especially if they choose Reason and or React and integrate it into their project, but for a test drive, requiring it is a large hurdle and a lot more to keep track of.
Reason's current distribution method is akin to Python requiring the installation of a C compiler toolchain so you can download the source and compile. To be sure, that's a wanted distribution method of an open source project like that, but they've rightly recognized that as the only distribution method it would hamper adoption, especially on platforms where that toolchain is harder to get set up.
I'm not even suggesting Reason go as far as that, exactly. I'm suggesting Reason ship a tarball/zip with a directory structure that includes Node and a set of pre-installed packages that provide Reason/Bucklescript. Installation instructions would include setting a few environment variables (altering PATH, telling Node where to look for packages). Or instead of Node/npm, provide Ocaml/OPAM with the correct packages pre-installed (although that might be slightly harder due to an increased amount of binaries as opposed to interpreted scripts as with JS). Or both in the same package, if it's not an excessive amount of work.
I understand it's a lot of work, but at a certain point, expecting prospective users to install a whole toolchain not for your language, but for the hosting language, just so they can test it out your language ends up being limiting. You've said that Reason isn't trying to be opinionated in what kind of app you're trying to build, but I think it's still being very opinionated in the exact process required to build that app. It's just that you allow for two opinions instead of one.
Hopefully that's clear enough, and you understand this comes from an actual desire to make Reason better. I spent a couple weeks in my spare time earlier this year evaluating whether Reason would work well for a new project I was starting, and I was extremely excited by the possibility of a shared front-end/back-end codebase that could be compiled to binaries when needed for performance reasons. That ended up not working out because I think the ecosystem isn't quite there yet for that exact need (e.g. a good framework and ORM that's mostly in Reason, and doesn't require converting from Ocaml to reason, etc), but a big takeaway I got from that was that as someone not invested in the Node module ecosystem that much, setting that up, and in a way that was repeatable and documented, is painful, and that ended up counting against Reason in the end because I would undoubtedly have to deal with that. That's not to say I wouldn't with what I'm proposing, but if I'm already on the Reason cheer squad that's less of a problem, language adoption-wise.
FWIW, Scala and Clojure require the JVM to work at all, and both are pretty popular.
My guess is that at Facebook there's too many smart people with differing opinions. Hence, we have to do an insane amount of research to construct the grab bag of pulling things together. And once that grab bag is complete, Facebook says "here's the new cool way to do it".
Ember on the other hand has happily given you the grab bag and has helped you support your production happens with the most professional, enterprise release structure.
Disclosure: Used to do only frontend development with Ember but switched jobs and now use React/Ember
Compared to react, ember is: slow (1), more difficult to learn (2), has a much smaller community (3) and, most important aspect to me, has a huge footprint (4). (I'm not against ember, it might have some nice aspects and features. It was really nice during the time when they released 1.0. But today I just think their are better options available)
1: https://auth0.com/blog/face-off-virtual-dom-vs-incremental-d... 2: https://guides.emberjs.com/release/ 3: https://github.com/facebook/react vs https://github.com/emberjs/ember.js 4: https://gist.github.com/Restuta/cda69e50a853aa64912d
But. When it comes to productivity (actually building something and maintaining it), I can achieve way more in Ember.
I find code written in ReasonML is 100x more maintainable than anything written in javascript, whether it's react or ember
Pros:
* Compile speed is mindblowing. As in, a big project compiles within a few seconds, and incremental compiles are instantaneous. The productivity gains of this cannot be overstated; you just don't get those moments anymore when you're waiting for webpack, your mind wanders off, lets just check HN real quick, ... no, you just stay in the flow. It's great. Elm, on the other hand, is sloooow.
* Type inference works really well. 9/10 times I don't even need to annotate anything and the compiler just knows. The only runtime errors I've ever run into were related to JS externals; the peace of mind knowing "if it compiles, it really works" is a game-changer even compared to TS.
* Error messages are (for the most part) fantastic and really helpful.
* Editor integration is solid, if a bit painful to set up at times.
* The ability to drop in the occasional chunk of dirty `[%bs.raw {...}]` is nice, and a clear advantage over Elm.
Cons:
* JSX support is a joke. You have to wrap every string in `(ReasonReact.String("bla"))`. You have to wrap arrays in `(ReasonReact.array(...))`. Passing children to components is supposed to be easy but will make you rip your hair out for sure. Etc., etc. But I'm sure it'll get there eventually.
* Refmt (the autoformatter) works pretty well but has lots of rough edges still. For instance, it removes all empty lines.
* State management isn't the greatest. Passing down props works alright but often it's just easier to keep things in a central Redux-ish store, especially because every. little. thing. needs to be passed down explicitly for the type checker to work.
* shouldComponentUpdate is hard-coded to `true` right now; there is no shallow comparison by default so everything always re-renders. Will be fixed medium- to long-term, I've been told. Workarounds exist but are pretty gnarly. Elm is much better at this.
* The standard library is a mess. ReasonML has its own, but forget about it; you'll fall back on Belt (the BuckleScript one), which 8/10 times has what you need but then is split into "Belt" parts and "Js" parts for unknown reasons and isn't always complete (e.g. I constantly need to convert from lists to arrays and back, because some utility functions only exist for one of the two, also for no real reason). There is piping syntax (|> and |.) but it's never clear which one to use; sometimes the data goes in first, sometimes it goes in last. You get used to it eventually, but Elm is a land of milk and honey in comparison.
* Lack of sugar: For instance, you'll end up making heavy use of option types, which are great obviously. Unfortunately there's no easy access syntax, so you end up with deeper and deeper levels of nested `Js.Option.getWithDefault()` and/or pattern matching.
* Documentation is meh overall. ReasonML is well documented, but it's only a very thin layer on top of BuckleScript. It took me hours of trial and error to figure out JS interop in practice, setting up "comparators" just to create a `set(int)` etc. You're effectively required to grok all of JS, Reason, Bucklescript, and OCaml at once, and draw from all of them to piece together your code. I think this will take another year or two to iron out.
Overall, I haven't found an option yet that truly works well. Elm is beautiful but slow and will drown you in boilerplate. ReasonML is fast and mighty but the UX isn't there yet. React+Redux+Typescript+Babel+Webpack have everything you need but it's a pain to set up and maintain, it's slow, and you can never trust it 100%.
(Edit: I should note that both Elm and ReasonML have amazing communities with very active, friendly and responsive dev teams that really and truly want the best for their languages. I have huge respect for both and I would encourage anyone to give them a try, just don't expect a perfectly polished experience yet.)
https://medium.com/w3reality/working-with-option-using-maybe...
In the end, however, it's more bolted-on pieces that should be part of the language if you ask me.
As in you can't override it? `shouldComponentUpdate` resolving to true in the absence of a custom method or PureComponent usage is standard React behavior: https://github.com/facebook/react/blob/7350358374036c0834ea6...
I should have been clearer: it'd be okay if `true` were simply the default, but the crux of the issue is that you can't even implement your own `shouldUpdate` as the function doesn't provide the previous props (like it does in React), so you have nothing to compare against – unless you laboriously store them yourself as state.
I was curious enough to glance at the React-Reason doc's and they say that `shouldUpdate` is passed a record called `oldAndNewSelf: {oldSelf: self, newSelf: self}`. Does that not have what is needed to implement the method without extra storage? For example, in standard React `shouldComponentUpdate` is passed `nextProps` & `nextState`, and since the method is in context of the previous render, we have everything we need to make the comparison without explicitly taking extra steps to store previous/next.
Maybe we are saying the same thing in different ways, I seem to be pretty good at that :).
The documentation is outdated, and the {oldSelf, newSelf} doesn't exist anymore. You had to use the `withRetainedProps` component for this to work, but that now results in an error message on compilation, telling you to use a reducerComponent instead.
I think it's something they intend to figure out a better solution for, but it's been put on the back-burner and meanwhile we're stuck with a non-solution. (Unless this is all wrong and I've been lied to and there's a better way, in which case, please enlighten me!)
Thanks for taking the time to clarify, and thanks for the original pros/cons post. I'm currently working on a mid-sized MVP project that is standard ES6 + Flow. I've recently been exploring our options for migrating to TypeScript or something like ReasonML. I've worked extensively with TypeScript, but am only starting to dip my toes into other options. You've been more helpful than just reading the documentation :)
Give it two years though, and both will be amazing.
This was my experience as well. Also, while it may theoretically be possible to compile native code with reason, the core focus of reasonml is javascript frontend development at the moment.
> On the native side, since Reason's just a syntax transform: yes, they work with Reason too. But the native workflow is currently work-in-progress and needs polish.
refmt formatting drives me a bit nuts, especially the.lines thing; it's steadily improving though.
One thing though - Belt is the WIP standard library (so OCaml), whereas JS is just bindings to JS, they're very different things. Belt produces much better JS when transpiled, and it seems a lot saner than the stdlib, but it isn't really anything to do with JS bindings.
And the pipe operator is |>, afaics |. is a typo on the Promises documentation page?
|> for reverse application, @@ for application, so
foo |> bar |> baz
and baz @@ bar @@ foo
are both the same as baz(bar(foo))
and they work excatly the same as in other ML languages someList
|. Belt.List.map(i => i + 1)
... because most Belt.List functions take the list as the first argument, and |> feeds its input as the last argument.Edit: apparently |. is BuckleScript, lots more discussion here: https://github.com/facebook/reason/issues/1452 ... just another instance of Reason still finding itself.
The opposite is having a conclusion at the end. That's used for opinions. A severe disagreement (not to say error) can be found faster if building an argument from the ground up.
If your conclusion is somewhere in the middle and scattered all over the place, you might want to highlight that indeed. I prefer stream of conscioussness for quick notes, but bringing it into a proper format can help to refine the argument.
I am using the approaching the mercenary approach to my career so I am interested if ReasonML would be a good long term bet. What do you guys think?
Oh yes, big thing is that interop with Reason is way simpler then in Elm.
[0] https://reasonml.github.io/docs/en/quickstart-javascript.htm... [1] https://reasonml.github.io/docs/en/global-installation.html
Thanks.
The main difference IMO is that Elm is not really a general purpose language, you must use the Elm architecture to use it. It is only meant for frontend web applications.
Reducer components don't solve the problem of a centralized/shared/common state and interactions between components.
1. A pattern to organize their state in that was testable and repeatable
2. A way to maintain global app state without prop drilling
I think that (1) is actually the reason that many people pick Redux in the first place. The amount of global, app-wide state in /most/ applications is fairly small IME. I think that the ReasonML team (which works semi-closely with the React team) is banking on things like Apollo, "suspense", the new context API and other things to further reduce the need for a global state store like Redux.
The ReasonReact devs are trying to follow the principal of least power in providing something that solves (1) for component-level state, which solves the 80%, and wait for the rest of the ecosystem to shake out for the rest.
We've been converting a large JS react app to Reason, and strongly typed props solves a lot of problems. Passing props down multiple children is no longer a burden, and you'll never have that "prop spreading madness". For anything that reaaally requires some sort of global state, you can use the new react context api.
The only danger of writing in reasonml is spoiling javascript for yourself
Curing yourself :)
Is the process like say converting to using Flow where you can just add it in where you want and the application still runs fine, or by convert do you mean write a whole new application and when it's complete then launch?
It is clothed in ALGOL's syntax, but underneath it is a LISP with functions that can be stored, curried, passed around, and composed on top of one another. It doesn't have a Lisp-y syntax and thus no homoiconicity nor macros, but it does have PPX. (https://whitequark.org/blog/2014/04/16/a-guide-to-extension-...)
The biggest one that Reason pulls however is its syntax. ML based languages (Hope, Haskell, Miranda, F#, Elm, Purescript etc.) have historically used the ISWIM syntax (refer Peter Landin: The next 700 programming languages), which was designed to be similar to mathematical notation. Reason ignores this great tradition and brings ML to the bread-and-butter programmer with a syntax that is more ES6 than ES6 itself.
Reason is also the first language where the four major thoughts of PL design comes together.
It packs ML's type system, which lets you create user defined types on data, unlike Simula/C++/Java/C# which commingle user defined types and classes. Interestingly enough, Stroustrup had worked with Pascal the grand-old language of commercial programming, before he went on a great rage and wrote CFront/C++. Pascal was statically typed similar to how Reason/OCaml is. It allows you to define new types that work simply on the shape of the data without the ceremony of classes. But Pascal's type system at the time was quite limited - Stroustrup referred to it as strait-jacketed in HOPL II (Prof. Wirth had meant it only to be a language for teaching after all). Simula's types and classes however appealed to him, and that paved way for the class-based type system in statically typed object-oriented languages ever since.
But this doesn't mean that Reason doesn't have support for object-oriented programming. Reason is OCaml, and the O stands for "Objective", a nod to its object system that knowledgable people like avsm has averred to as fantastical.
The Lispy roots of Reason/OCaml makes it functional, which in simple term means value-based programming, primitives like fold and map defined with recursion, and linked-lists are used for almost everything, except when it isn't. Xavier Leroy, who is both the author of Coq and OCaml is a pragmatist - the OCaml compiler code is choke full of mutation and OCaml has strong support for imperative programming but tastefully cordoned with an explicit syntax.
It is a langauge that mixes imperative, procedural, object-oriented, and functional paradigms, sitting just at the confluence of the great PL traditions that includes ALGOL, Simula, LISP, Modula, ML, and ES6 (!). It is functional programming for the masses, finally delivered!
Here's the functor: https://github.com/tonyhb/reasonable-native-budget/blob/mast...
Here's the functor being applied: https://github.com/tonyhb/reasonable-native-budget/blob/1f69...
Caveats: haven't committed some time and it was my first ReasonML/React Native app.
Not trolling, besides lack of types it seems like a pretty darn solid, ubiquious, elegant/straightforward language to get things done.
For people with strong opinions about this, what is so horrifying about Javascript to you?
It is easy to write flaky code in JS and rarely the language helps you to go for the elegant solution. On this front ReasonML brings huge value to the table.
You can do a lot of funky things with javascript, and it's easy to convince yourself that because you can do it, you should do it. With reason I've found I can achieve pretty much anything with a far more concise set of tools, and I can't hack my way around problems - I'm forced to deal with them the correct way.
Being forced to do things in a certain way has a massive benefit, and encourages consistency. You can look at 5 different javascript projects and each one of them will most likely be completely different.
To write nice JS, you have to be disciplined and very cognizant of what you're doing. And then you have to deal with the rest of the ecosystem, which is massive and of varying quality.
I recently ran into mutable date types in momentJS, which is a frustrating gotcha - it's not flip the table lose your shit annoying, but it's simply a non-existent issue in other languages.
Alternatives: Elm, Purescript, etc.
Edit: I recommend stopping and thinking a bit about ethics of technology choices before reactively downvoting.
What if Facebook pays a developer to work on another popular library like Vue.js? Should we consider stop using Vue?
Companies release these libraries because they benefit the companies. The successes of projects like React help Facebook.
I think that people who believe that Facebook is an unethical company should consider alternative technologies on new projects whenever possible.
The success of projects like Haskell and OCaml help Facebook too. Should we consider not using those technologies?
It seems the only difference you can point out is that Facebook "owns" React. But this doesn't make any practical difference when it comes to open source. It's like public transportation. If Facebook sponsors a high-speed railway that benefits both them and two cities, so what? That's great for everyone.
If you were talking about a paid SaaS run by Facebook, then I would agree with you. That would be more analogous to supporting Uber than supporting public transportation.