Summary
JRuby core developer Charles Nutter introduces a version of Ruby that uses type inference to help the compiler attain better performance. Nutter's experimental language is called Duby, and its performance using primitive types compares to that of Java.
Advertisement
Modern statically typed languages, such as Scala and C# 3.0, provide type inference to reduce the amount of finger typing required of a developer. While dynamic languages often require less finger typing, they are also much harder to optimize during compilation and performance, due to the lack of type information.
In a recent blog post, Duby: A Type-Inferred Ruby-Like JVM Language, JRuby core developer Charles Nutter introduces an experimental language that aims to combine some of the best features of Ruby with the performance attainable in statically-typed code:
We've long wanted to have a "Ruby-like" language, probably a subset of Ruby syntax, that we could compile to solid, fast, idiomatic JVM bytecode. Not a compiler for Ruby, with all the bells and whistles that make Ruby both difficult to support and inefficient to use for implementing itself. A real subset language that produces clean, tight JVM bytecode on par with what you'd get from compiled Java. But better, because it still looks and feels mostly like Ruby...
There are many Ruby features that influence performance negatively even when you're not using them. It's very difficult, for example, to optimally store local variables when the local scope can be captured at any time. So either you rely on tricks, or you store local variables on the heap and deal with them being slow...
When working with a statically-typed language, you can eliminate all of this. In Java, for example, you have both object types and primitive types; primitive operations are extremely fast and eventually JIT down to machine-code equivalents; and the feature set is suitably narrow to allow current JVMs to do amazing levels of optimization...
But of course Java has its problems too. For one, it does very little guessing or "inferring" of types for you, which means you generally have to declare them all over the place. On local variables, on parameters, on return types. C# 3.0 aims to correct this by adding type inference all over, but then there's still other syntactic hassle using any C-like language: curly braces, semicolons, and other gratuitous syntax that make up a lot of visual noise...
Nutter's main motivation in introducing static types, and primitive values, in particular, is performance. He illustrates the performance problems with dynamically-typed code using the following example:
class Foo
def fib(n)
if (n < 2)
n
else
fib(n - 2) + fib(n - 1)
end
end
end
Wouldn't be nice if we could take the code above, add some type inference logic, and turn it into a fast, static-typed language?
Nutter then introduces his experimental language that aims to provide just that. Using this language, the above code is written as follows:
class Foo
def fib(n)
{n => java.lang.Integer::TYPE, :return => java.lang.Integer::TYPE}
if (n < 2)
n
else
fib(n - 2) + fib(n - 1)
end
end
end
Nutter notes that this language is very experimental, but that it may point the way to languages that make better use of type inference:
My working name for this is going to be Duby, pronounced "Doobie", after Duke plus Ruby. Duby! It has a nice ring to it. It may be subject to change, but that's what we'll go with for the moment...
Currently, Duby supports only the features you see here. It's a very limited subset of Ruby right now, and the subset doesn't support all Java primitive types, for example, so there's a lot of blanks to be filled...
I guess you're after something that looks more like Ruby. By the way, this algo isn't tail recursive, so larger numbers passed in could cause other performance problems. A little searching on the web and I found this tail-recursive algorithm:
class FiboTail {
def fib(n: Int): Int = {
def aux(n: Int, next: Int, result: Int): Int = if (n == 0) result else aux(n - 1, next + result, next)
aux(n, 1, 0) } }
The bytecodes for this one show just one call to the auxiliary function aux (which I defined as a local function inside fib):
Sorry to crash your party with Scala code. I'm sure you're looking for a more Ruby-like syntax, but when you started talking about wanting a language with concise expression, type inference, and optimized bytecodes for the JVM, it rang a bell for me.
The nice(or bad) thing about ruby though is that instead of overflowing fixnums morph into bigints transparently, which isn't the case of the Duby code I guess. I don't know how this behavior can be annotated with types, probably to not lose the efficiency, using Either[Integer,BigInteger] is a solution in scala for eg but this leads to two paths in the code, and it will infest all the library. While morphing at runtime seems to be unefficient. (Wonder how the haskell guys handle this even though I know that problems can arise with Int, Integer, fromInteger ..)