The JRuby Blog

The Ruby Programming Language on the JVM

Why Isn't JRuby Implemented in Ruby?

Published by Charles Oliver Nutter on

As Ruby implementations mature and Ruby code runs faster and faster,
the question starts to come up again and again: why not implement
more in Ruby? There’s many good reasons to do it: easier for Rubyists
to maintain and contribute; no native library dependencies; better
visibility for debuggers and profilers; consistency all the way into
the core… the list goes on. As you’d expect, we often get this
question about JRuby itself, given that JRuby now runs Ruby code very
fast and most Rubyists would rather not read or write Java code. So
why isn’t JRuby implemented in Ruby?

The answer: it’s complicated.

Ruby is Not the Best Language for Everything

The simplest answer is perhaps the most obvious: Ruby isn’t always
the best tool. There’s many cases where Ruby’s dispatch overhead,
coercion protocols, arbitrary-precision integers, or mutable types
get in the way of expressivity or performance. What makes Ruby
“beautiful” sometimes obscures intent from either the programmer or
the underlying platform.

Performance is obviously a key issue. Sometimes, you really need raw
native maths or byte arrays and Fixnum, Float, and Array introduce too
much overhead. Using Java gives us a “known quantity” when it comes
to performance.

Now of course I’m not saying Ruby won’t continue to get faster and
faster. JRuby on Java 7 has started to approach Java performance for
many algorithms, and as JRuby and the JVM improve, the gap will
continue to narrow. But optimizing a dynamic language is challenging
even on the best VMs, so it’s nice to have that “known quantity”
when performance is critical.

JRuby’s a Ruby Implementation and a JVM Language

Then there’s also the matter of interfacing with statically-typed JVM
libraries. JRuby makes heavy use of JVM libraries for much of its
internals. That has meant we don’t have to implement our own
collections, IO subsystem, JVM bytecode generator, YAML 1.1, and
much more. Of course some of these could be called via our Java
integration layer, but there’s always a bit more overhead down that
road than by having Java calling Java.

Almost as important is the fact that nearly all of JRuby’s core
classes can be called as normal Java classes from any piece of JVM
code. Java integration is a two-way street, and having Ruby-only
implementations would make one direction more challenging to
support.

Don’t Throw Out the Baby

Moving to Ruby for new code is easy enough, but moving existing
code to Ruby would mean throwing out known-working, battle-tested,
optimized Java implementations for something completely new. Any
good developer knows that a “big rewrite” seldom ends well, so
we’d be doing a great disservice to the thousands of JRuby users
out there by making a wholesale move to Ruby. There’s no measure
of benefit (from rewriting in Ruby) that would outweigh losing
years of maturity in our codebase.

Inertia

Inertia may be the biggest motivator here. The JRuby committers
are very familiar with Java and with the current implementation.
They also may know Ruby very well, but change is always
accompanied by some measure of confusion.

Most of the JRuby core team manages a subset of JRuby; Tom Enebo
works on parser, interpreter, and the new IR compiler. Nick Sieger
works on libraries like activerecord-jdbc and jruby-rack. Wayne
Meissner works on our native library integration layers like FFI.
I work on compilation, optimization, and Java integration. Do we
force everyone to start writing in Ruby when they may prefer to use
Java?

Developer Pool

As much as Ruby has grown in recent years, there are still far
more Java developers in the world. They may not love the
language, but they represent a tremendous potential pool of
contributors to JRuby. Yes, it’s true that a Java developer is
probably less likely to contribute to JRuby than a Rubyist…
but there’s still a hell of a lot of them out there.

There’s also a very large portion of JRuby users (perhaps even
a majority) that are either primarily or originally Java folks.
Having a mostly-Java codebase means they can more easily
investigate bugs, integrate JRuby into their JVM-based
applications, and generally reason about how JRuby actually
works. That’s very powerful.

But We Still Want to Do It!

Even though I list a bunch of reasons for having a mostly
Java codebase, we do recognize that Ruby is an excellent tool
for both writing apps and for implementing Ruby’s core. And
we have always intended to make it possible for JRuby to use
more Ruby code as part of the core implementation, even if
we never do a wholesale rewrite.

To that end, I’ve started restructing the (admittedly small)
Ruby-based “kernel” of JRuby into a structure that’s more
approachable to Rubyists that want to contribute to JRuby. The
restructured Ruby kernel is under src/jruby,
and while there’s not much there right now we’re willing to
accept new code in either Ruby or Java. If it becomes a
performance or integration problem, we may rewrite that code
in Java…but having a working implementation in Ruby is far
better than having nothing at all.

Whither Rubinius?

You might be asking yourself “why not just use Rubinius’s
kernel?” We’ve always hoped (maybe intended) to use as much
as possible of Rubinius’s mostly-Ruby kernel in JRuby, if
at some point that seemed feasible. With recent performance
improvements in JRuby, that day may be approaching. We would
need to patch away anything that’s Rubinius-specific, but I
have toyed with how to start using Rubinius’s kernel in JRuby
several times in the past few years. If we can borrow their
code for missing or poorly-implemented parts of JRuby, it
would be stupid for us not to do so.

It’s also worth pointing out that Rubinius has very few of the
challenges JRuby does when it comes to integrating with an
existing platform. Rubinius was designed as a Ruby VM alone,
so there’s no equivalent to Java integration. When Rubinius
wants to utilize native libraries, they do what we do: write
wrapper logic in C/++ (equivalent to JRuby’s Java code) or bind
those libraries with FFI (similar to but more basic than our Java
integration). And Rubinius exposes no native, statically-typed
API to its implementation classes.

How Can You Help?

Right now we’re looking to use Ruby mostly for missing features.
Since we’re still in the midst of filling out Ruby 1.9.2 and
1.9.3 features, that’s a good place to start.

We use and contribute to RubySpec, just like Rubinius does,
so you can easily find missing or broken features by looking under
spec/tags
in our codebase.

(RubySpec supports the concept of “tagging”, where known-failing
specs can be “tagged” until they pass. This allows implementations
to maintain a “watermark” of passing specs over time, and allows
contributors to easily see and fill in implementation gaps.)

You’ll want to fetch the revision of RubySpec we currently test
against by running rake spec:fetch_stable_specs (or ant
fetch-stable-specs
; git must be installed in both cases), but
after that you can run specs using spec/mspec/bin/mspec
in the usual way.

And of course if there are Ruby 1.8 or 1.9 features that are
weakly specified by available tests, we strongly encourage
you to contribute specs directly to the RubySpec
project, so that all implementations can benefit.

We hope that making it easier to implement JRuby using Ruby
code will make it more approachable to Rubyists, and we’re
looking forward to helping you craft your first pull request! Stop
by #jruby on Freenode IRC this week and we’ll help you out!