The best kittens, technology, and video games blog in the world.

Saturday, February 24, 2018

Opal - Ruby on Javascript VM - What's still missing

Freya by CarlosDemarte from flickr (CC-NC-ND)

Javascript VMs are everywhere, and environments running them such as browsers and phones look likely to remain the dominant way for users to interact with software for very long time. This led to explosion of popularity of Javascript - the native language of that environment.

But Javascript is a dreadful language. It started a dirty hack, which just so happened to be positioned right and benefited from browsers becoming universal user interface. It can't even ever get properly fixed like let's say Python 1, due to unique backwards compatibility cost.

Fortunately just as you don't need to write Java to run on JVM, or write x86 assembly to run on x86 hardware, it should in principle be possible to run on Javascript VM without writing any Javascript.

So far it's been mostly "Javascript++" style languages like CoffeeScript, JSX, TypeScript, transpiling to older versions of JS and so on. It's not nothing, but all of them are trying to make Javascript at best barely tolerable.

So why not just write in a real language, and compile it to whatever Javascript VM wants?

Opal attempts to do just such a thing, letting you run Ruby in a browser. It's a somewhat different dialect of Ruby, most notably with immutable Strings, Symbol/String unifications, single numeric floating-point type, and a few other compromises to make it run better on Javascript VM, but it's mostly good enough.

Unfortunately I don't think it's ready for serious use just yet. Here's the top 10 things it's missing, in roughly the order of importance.

Out of the box sourcemaps

When an exception happens, the most important information is where it's coming from, and the second most important is the entire stack trace. Exception type and message are usually not terribly informative anyway. With Opal, you get none of that, just some pointer to compiled code which looks nothing like what you wrote.

It's sort of true that it's also a problem with CoffeeScript, or JSX, but with such languages you can usually figure out what's up from compiled Javascript with some practice. Opal to JS mapping is too big of a change, and lack of source maps makes debugging extremely tedious.

Opal absolutely needs sourcemaps, out of the box, in every environment. That's the single barrier to productive use.

Decent test framework

Opal has opal-rspec and it's just not good enough. It doesn't work with latest version, doesn't give any meaningful information why a test failed, doesn't have any mode other than "run all tests", and is weirdly slow on top of it all.

This isn't even all that surprising, as Javascript testing is decade behind Ruby, and sourcemap issue on top of that makes it even worse. It's a huge productivity killer.

binding.pry

One of best features of ruby is binding.pry. For that matter one of the best features of Javascript is debugger, which is basically less powerful version of binding.pry.

With Opal, you get none of that. You can use `debugger` and try to figure out things from compiled Javascript code, but that's not exactly a pleasant experience.

Support for Ruby regular expressions

Opal doesn't support Ruby regular expressions, just throws whatever you write onto JS regexp engine, and it's not even close to good enough. Even something is simple as /\A\d+\z/ will completely fail - and even worse it will fail quietly without raising an exception.

Of course you can write JS regexps, but then you lose any hope of sharing same codebase between regular Ruby and Opal Ruby.

As an absolute minimum it should raise a damn exception when it sees something it can't deal with.

Support for Time.parse

Another thing which turned out to be far more annoying than expected is dealing with timestamps. Ruby has Time.parse, which is pretty decent at handling most commonly used timestamp formats. Opal will instead happily use raw Javascript constructor, and quietly give you NaN timestamp object.

Like with previous point, the absolute minimum is to raise a damn exception here, but something resembling Ruby's Time.parse would go a long way towards making Opal useful.

pry-rescue

As followup to some previous points, something that lets you jump straight into pry on unhandled exception would be pretty damn good. Especially if it also worked in the test suite.

Precompilation of libraries for better start time

Ruby doesn't have separate compile phase - it just executes code on a fresh VM, and that code setups various methods and classes via metaprogramming. Then it runs the application itself.

That works fine on Ruby VM, but most environments perform really poorly in this mode. Startup time of Ruby on JVM with JRuby or Javascript VM with Opal is thus fairly bad.

This is probably the most difficult thing to do, but right now any Opal code using a medium-sized wrapper like opal-d3 will have a lot worse startup time than native javascript version.
Once it starts, overhead isn't too bad, but this really needs to be solved for production use.

Better interoperability with plain Javascript objects

This is another point about debugging, but let's say I get a Javascript object somewhere, and I want to figure out what the hell it is, or just dump it to the console. Currently it's going to crash with a helpful message telling me that Javascript objects don't implement inspect method. Well, true, but it's awful debugging experience, as realistically we'll be mixing Opal Ruby code with native Javascript libraries in most use cases.

A site like codepen.io

One of the nicest things about frontend coding is that it's possible to quickly code something in the browser, without going through all the hassle of setting up local environment, and share that with others. There's a lot of sites like codepen.io which help with this low-overhead quick coding.

Opal will definitely need a site like that, or be supported by one of them.

A Killer App

And finally we get to the usual "Killer App" point. I put it last intentionally - because unless most of the previous points are addressed, there's no killer app which could possibly work.

No comments: