JavaScript - - By Eric Elliott

The Future of Programming: WebAssembly & Life After JavaScript

Recently we heard that the web platform is getting a new browser-native compile target called WebAssembly (see What is WebAssembly: The Dawn of a New Era for details). Wasm makes the web platform a more attractive compile target for other languages. That leaves us wondering what might come after JavaScript.

JavaScript has some great features, but I sincerely hope we move on as soon as possible. JavaScript is great and it taught us a lot, but programming will evolve. There will be life after JavaScript.

I’m not a fan of C++ (I’m a recovered user and abuser of C++), but I am a fan of many things built with it, including the Unreal Engine, which runs great when compiled to JavaScript. There’s no good reason to write everything in JavaScript forever. It’s time to look to the future.

I’ve been using and really enjoying CoffeeScript from time to time, which helped inspire a lot of the new ES6 features. CoffeeScript feels much simpler than JavaScript. It has a more concise syntax, yet adds a lot of expressive capability that did not exist in ES5. Of course, I’d simplify CoffeeScript even more by ridding it of the poisonous class keyword. I like to fiddle with Haskell, too. But these languages represent the present.

So what could we do next?

Whatever catches fire after JS, I hope it has these features:

  • Built-in immutable data structures and immutability by default.
  • Literal forms for objects, collections, and primitive types.
  • A better default number type for most use cases.
  • Custom structural types, including good support for low-level binary types (bonus if we get definable literal syntax).
  • Lambdas with closures.
  • Functional utility belt built-in. Similar to the RxJS Observable API.
  • Native support for object and factory composition. Something like built-in stamps.
  • Realtime guarantees for low-latency processing & precision scheduling for apps like gaming, music & video production, DSP, scientific applications, synchronization of distributed systems, etc…
  • Minimal syntax, similar to Ruby, Python, or CoffeeScript. Now that I’ve had a chance to get comfortable without curly braces and semicolons everywhere, I feel a lot less confined by syntax.

First-class Support for Reactive Programming

Baked into the language, a simple, consistent API around all of these:

  • Streams.
  • Continuous data sources (UI inputs, time, vector images, etc…).
  • Collections including arrays and objects.

This API should be used the same way regardless of whether data flows through the functions one value at a time (i.e. iterables / generators + yield), in response to emitted events (e.g. Node-style streams), or continuous data sources such as audio, electrical signals, UI inputs, sensors, even time-independent things like vector graphics, etc…

A built-in API like this could wrap all types, which has the potential to simplify the syntax, as well.

For a really insightful perspective about how all this reactive stuff works, check out A General Theory of Reactivity.

Better Tooling

Unreal Engine 4 Blueprint
Unreal Engine 4 Blueprint

  • A fantastic visual IDE for easily modeling and visualizing reactive relationships in the system. Think NoFlo with a much better UX.
  • Time-travel debugging (example with JavaScript) enabled by immutable data structures. Time-travel debugging lets you easily shuttle back and forth in the history of your live, running program.
  • Compile to JS & wasm great support for browsers and Node.
  • Better analysis tools both static and runtime / dynamic. Specifically designed to help make programs more predictable by analyzing reactive dependency graphs. These could create great visual reports, too, including complexity reports, and marble diagrams to help you further understand and predict the behavior of your program.

Marble diagram for .merge()
Marble diagram for .merge()

Visual IDEs Will be the Norm

Those familiar with flow-based/dataflow programming will rightly tell you that it’s nothing new. Visual programming tools have been around for decades, and have so far been unable to replace text-based programming.

What will push this over the edge is a radical rethinking of how to model programs visually that will reduce the visual clutter and wiring overhead that plagues most flow-based programming solutions.

Most of the innovation in this space is not happening in programming environments at all. Instead, it’s happening in production applications where data flow modeling is the primary task. Nowhere is this better illustrated than in audio production apps.

Audio production apps typically route raw audio through a network of effects processors. In programming terms, you could think of an effects processor as a functional map: a pure function called for each element in a list, where those elements correspond to an audio sample slice.

Most audio apps model this process visually by simulating the real machines and cables in skeuomorphic fashion. A skeuomorphic user interface is one that attempts to emulate the user interface of the original object being modeled in software.

The problem with skeuomorphic design is that it faithfully reproduces most of the user interface clutter and inefficiencies of the original. In data intensive applications such as audio production, that clutter looks remarkably familiar to programmers: The wires look a bit like spaghetti — something all good developers know we should avoid.

BayAreaModularMeet — George P. Macklin — (CC BY-SA 2.0)
BayAreaModularMeet — George P. Macklin — (CC BY-SA 2.0)

But recently, software such as Ableton Live and Renoise have found clever ways to rid themselves of wire clutter completely using channels and chains.

An Ableton Live effects channel. Data flows left to right.
An Ableton Live effects channel. Data flows left to right.

In other words, data flows through channels, and each channel consists of a chain of effects. No wires are needed, because the effects are applied in sequence.

Renoise channels, each with an effects chain. Data flows top to bottom.
Renoise channels, each with an effects chain. Data flows top to bottom.

In code, a channel might look something like this:

const channel = input => {  
  return input  
  .filter()  
  .gain()  
  .limit();  
};

export default channel;

If you think about this carefully, it becomes clear that you could do a lot more than audio & Digital Signal Processing (DSP) with this model. For example, you could use it to visually model routes and middleware in Express, where routes are represented by channels, and middleware is represented by effects:

A hypothetical Renoise-inspired UI to program routes.
A hypothetical Renoise-inspired UI to program routes.

Of course, this implies that each effect in the chain needs to act on inputs with a shared API. That’s where functional and reactive programming concepts shine. It’s easy to provide a unified API over any type of collection, including a stream of network requests. In functional programming, those wrapping APIs are called functors. In plain English, a functor is something that can be mapped over.

If this sounds a bit like science fiction, take a look at Treeline. It does something very similar today. Take a look at this POST /signup route modeled in Treeline. It encrypts the user’s password, then creates the user model, then responds with status 200 OK. Each of those steps could be thought of as a channel effect:

Treeline: Data flows top to bottom.
Treeline: Data flows top to bottom.

Genetic Programming

Genetic programming is the process of simulating nature’s evolution system by producing populations of candidate programs and filtering out the programs that don’t pass the tests. The candidates that do pass the tests survive and form the basis of the next generation.

Genetic programming offers the possibility of continual, automated improvement on hot code and critical algorithms. There is real potential to point a genetic programming algorithm at a git repository and automatically push new generations of software to production when the population improves on prior deployed versions.

AI-Assisted Programming

Scott Ingram — Dual Neuron (CC BY-NC 2.0)
Scott Ingram — Dual Neuron (CC BY-NC 2.0)

Strong AI is here today. Many AI systems are beating humans at our own games across a variety of categories. One major advantage of AI is that it is capable of analyzing a large variety of alternatives very quickly. AI could examine the code you write, search for potential bugs & vulnerabilities, recognize patterns that could be abstracted, suggest tests that need to be written, or even recognize patterns in genetic programming populations and automatically adjust population and environment parameters to fine tune genetic programming algorithms.

In other words, long term, AI has a very real chance of becoming an invaluable programming tool. In fact, it’s easy to imagine a future where AI can produce programs without any human assistance at all.

Conclusion

Whatever comes next, expect it to be a quantum leap in technology and innovation. The future will be here sooner than you think.

Sponsors