Flow. Why, oh why – one more language?
Every day, a new programming language is announced. Why do we need one more?
- Historical context & milestones
- Why is flow relevant now when so many other things just work?
- UX is the biggest feature
- Flow vs HTML5
- Flow vs multi-paradigm languages
- Flow vs other functional languages
Flow was conceived almost 9 years ago. The world was very different then. At that point in time, HTML5 did not really exist. The only way to run complicated code on the browser was using Flash.
Flash is obsolete now, but Area9 as a business needed a way to write software that ran in Flash, as well as iOS and Android. Our adaptive learning products did a lot of crunching of data on the client to calculate the best adaptive path for students. That would not scale to the millions of users on the server-side. So we had no choice but to use Flash, but we also knew that Flash would die in the long run.
We tried to hack on Haxe to do this for a long time, but we could not get it to perform well enough on the memory-constrained mobile devices back in those days. Haxe is a great language – and we still use it for the JS runtime in flow – but it turned out to be too hard to get it to work well on the first generations of iPhones and Android phones. (Haxe has come a long way since then, and would work well now.)
But as a business at that point in time, our options were limited. This was before apps on smartphones was really a thing. We were a startup, and our customers would not pay for us to develop the same thing three times. We understood that it was crucial to be on the mobile devices, because it would be the future. So it left us with little choice: Either we ignore the mobile platforms and lose in the future, or we try to fix this problem for ourselves.
So we defined the problem to be this: Design and implement the smallest possible language we could, which would solve the task. The goal was to allow complex, but easy to use and beautiful UIs to be written safely and quickly for all major platforms. The result was flow, and it turned out to work great.
As we started to implement our learning software in flow, we expanded and improved the capabilities. Some key points in time are listed here:
- August 2010: The very first flow program ran on Flash & HTML5
- November 2013: First app approved in iOS store
- April 2014: First app approved in Android PlayStore
- November 2015: Flash was retired, flow in the browser completely migrated to HTML5
- January 2016: Google Material guidelines implemented
- November 2016: JIT for x64 was added
- May 2018: Self-hosted compiler written in flow itself
- April 2019: Initial open source release
So over these 9 years, flow was evolved to be a mature, reliable platform. Code in flow is used by millions of users, and it has proven itself in real life.
Why is flow relevant now when so many other things just work?
The main reason is that flow is a stable, mature platform that not only provides a programming language, incremental compiler, multiple backends, but also comes with a capable standard library including a best-in-class UI library.
Flow is very light on syntax, easy to learn, read and write, and is based on clear semantics in the functional family of languages. It uses a C-style syntax which is familiar to most programmers.
It is a practical language, which is statically typed with type inference, but also supports dynamic typing when required. Out of the box, it is safe and crash-resistent. It encourages functional code, but does support references and side-effects when you need it.
So on it’s own, flow is a nice language, which will hold its own against many others. It is simple, easy and has proven itself. See below for more specific comparisons to other languages in terms of the language itself. But the biggest selling point is the support for building cross-playforms UIs with simple code.
UX is the biggest feature
The main reason why flow is relevant now is due to the standard library for UIs, called Material. This is the fourth generation of this kind of library we have designed and implemented over the years. It is the result of many years of experience with what really is important when you are writing cross-platform apps that need to work in browsers, on tablets & phones using keyboard, mouse and touch.
It is one thing to design a UI toolkit that works for yourself and a small team of programmers, harder to design and refine a toolkit, which allows hundreds of programmers to work together and quickly and safely produce a coherent, beautiful user interface. Doing this without a big staff of graphical designers and UI/UX experts to design everything in detail is hard.
So in our experience, it is not uncommon that a flow implementation takes 2-4 times less code than in HTML+CSS+JS, or even more when using the standard frameworks with C#, Swift or Java. And on top, very often, the initial implementation by the developers is also good enough that it looks good and is polished without many iterations required to make it production quality.
Flow as a functional language also means that the UI code is highly compositional, and promotes a lot of reuse. In short, this means that the productivity for building apps is very high, while not having to compromise on the quality of the user experience.
Flow vs HTML5
HTML-based technologies ride on very powerful capabilities of the browsers, but have the challenge that the frameworks change frequently. It seems the most popular framework changes every two years when something new comes along. Flow has been stable for many years, and has proven itself to be capable of competing with the best frameworks out there. If you learn flow, you probably do not have to learn something new next year.
The same takes 83 lines in flow.
So flow takes 83 lines versus 200 lines in React. And significantly, in flow, you do not have to split the code across three different languages. This makes it easier to learn and write. The exact same flow code can run as native apps for iOS and Android, and will look and behave the same.
So our claim is that the total complexity of an implementation of a UI is shorter in flow. It is easier to learn, since it only involves one, simple language with very few gotchas. In our experience, it also scales better to beautiful, responsive and adaptive UIs with lots of complicated interactions. Since all the code is in one language, it is easier to reason about and reuse. And on top, it can run as native apps as well.
Flow vs multi-paradigm languages
Many languages, such as Java, C#, C++ and Swift are multi-paradigm languages, which support both OO, functional and imperative paradigms. They come with great libraries, and are used by millions to produce great apps that we all use everyday.
If we compare flow to these, the biggest difference is that flow code tends to be shorter and easier to understand. This is due to a few things:
Light-weight, powerful expression-based syntax. The grammar for flow is 255 lines including comments. There are 25 keywords.
The type system is simple, but still supports type inference, subtyping and dynamic typing. Abstract data types allow natural modelling of data with strong type check from the compiler.
Coherent standard library. Since flow is a functional language, the standard library has been designed around this paradigm. In particular, we make heavy use of Functional Reactive Programming to handle interactivity. This means pieces fit well together, and is highly compositional even for UI code.
Code is often easy to understand in part because you are not faced with a mix of multiple paradigms and their interactions. There is no overloading, no implicit conversions, null-pointers, or method overriding. When you read some code, you can see exactly what it does right there, since syntax and semantics are aligned and does not change depending on the context.
Flow considers HTML as a first-class target. The above languages can not naturally run in the browsers. Some provide experimental WebAssembly targets, but those require big runtimes and thus a big download. Surprising, they also typically run many times slower than JS, and the UI toolkits are not ported to the browser. So code in these languages will not easily allow you to write multi-platform code which includes the browser as a target.
Except for the last benefit, many of these benefits are shared with other functional languages, such as ML or Haskell. So how does Flow compare to those?
Flow vs other functional languages
Flow is squarely in the ML-family of languages. It is eager, strongly typed with type inference with references for side-effects. As such, it is closest to ML, OCaml, F# and similar languages.
Another difference is that flow intentionally avoids some parts of ML that we consider hard for beginning functional programmers to learn and use. In particular, flow requires parenthesis for function application:
f(1, 2) versus
f 1 2. This helps disambiguate whether something is an argument to this or that function, and while it requires slightly more typing, it helps clarify what the intent is.
Functions are first-class in flow, and there are unnamed lambdas, but flow does not support currying. Currying is a very powerful technique, which is often considered a key part of the functional paradigm. So why do we not support it in flow? The key reason is that currying is hard to understand as a novice functional programmer, and can make the code harder to read. You are sometimes in doubt about what the type and meaning of a sequence of symbols is. After having 9 years of experience with this decision, we have not changed our mind on this. Currying is not used often enough that it warrants a syntactical shortcut that complicates understanding compared to writing a short lambda that does partial application.
One key benefit of ML and similar languages is strong pattern matching. Flow supports simple pattern matching, but not nested patterns nor guards. This is primarily due to the desire to keep the language simple. The primary flow compiler is self-hosted in flow itself, and pattern matching would certainly help shorten some code. But outside of compilers, our experience is that it is not that commonly used. That said, pull requests to add this would be welcome.
In general, flow code will typically be slightly longer than code in ML or especially Haskell which can be really succinct. This is partially intentional, because we believe too succinct code can quickly become too hard to understand for non-experts.
For the same reason, the type system of flow is a relatively simple Hindley-Milner without monads or type classes. It does support subtyping, and dynamic typing. This seems to provide a sweet-spot of safety and expressivity. Subtyping helps a lot when writing compositional UI code. UI components share so many aspects with subtle differences that subtyping helps make the API interface more orthogonal and easier to understand and learn, while still having the benefits of strong typing.
So in short, if you like functional languages, and want to make nice apps with rich UIs, flow is a great choice. It is simple, proven, comes with a mature and capable UI library, and runs native on the web with a small runtime, as well as on mobile devices as native apps.
Make up your own mind
We could also compare flow with scripting languages such as Python, with Lisps, Prolog, and an infinite amount of other languages. The benefits of flow will mostly be variations on the themes above. So instead of boring you with all of that, we invite you to check out the GitHub flow repository, give it a spin, and make up your own mind!
– Asger Palm, 26th of April 2019