The Artima Developer Community
Sponsored Link

Ruby Community News Forum
Ruby with Type Inference

2 replies on 1 page. Most recent reply: Mar 12, 2008 12:33 AM by Ramzi BEN YAHIA

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 2 replies on 1 page
Frank Sommers

Posts: 2642
Nickname: fsommers
Registered: Jan, 2002

Ruby with Type Inference Posted: Mar 10, 2008 2:45 PM
Reply to this message Reply
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...

What do you think of Duby?


Bill Venners

Posts: 2284
Nickname: bv
Registered: Jan, 2002

Re: Ruby with Type Inference Posted: Mar 11, 2008 12:58 AM
Reply to this message Reply
Well, I couldn't stop myself. I posted this as a comment on Charles' blog:

Just for the record, here's what it would look like in Scala:


class Fibo {
  def fib(n: Int): Int =
    if (n < 2) n else fib(n - 2) + fib(n - 1)
}


The bytecodes look similar to those generated by the Duby version:


public int fib(int);
Code:
Stack=4, Locals=2, Args_size=2
0: iload_1
1: iconst_2
2: if_icmpge 9
5: iload_1
6: goto 24
9: aload_0
10: iload_1
11: iconst_2
12: isub
13: invokevirtual #16; //Method fib:(I)I
16: aload_0
17: iload_1
18: iconst_1
19: isub
20: invokevirtual #16; //Method fib:(I)I
23: iadd
24: ireturn


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):


public int fib(int);
Code:
Stack=4, Locals=2, Args_size=2
0: aload_0
1: iload_1
2: iconst_1
3: iconst_0
4: invokespecial #25; //Method aux$1:(III)I
7: ireturn

The tail recursion inside aux gets optimized away into a loop:

private final int aux$1(int, int, int);
Code:
Stack=3, Locals=5, Args_size=4
0: aload_0
1: astore 4
3: iload_1
4: iconst_0
5: if_icmpne 10
8: iload_3
9: ireturn
10: iload_1
11: iconst_1
12: isub
13: iload_2
14: iload_3
15: iadd
16: iload_2
17: istore_3
18: istore_2
19: istore_1
20: goto 3

This tail recursive one seems to give the same answers as the non-tail recursive one (for small numbers):

scala> :load dubious.scala
Loading dubious.scala...
defined class Fibo
defined class FiboTail

scala> val fibo = new Fibo
fibo: Fibo = Fibo@ee1c42

scala> val fiboTail = new FiboTail
fiboTail: FiboTail = FiboTail@616fde

scala> for (i <- 1 to 10)
     |   println(fibo.fib(i) + " " + fiboTail.fib(i))
1 1
1 1
2 2
3 3
5 5
8 8
13 13
21 21
34 34
55 55


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.

Ramzi BEN YAHIA

Posts: 5
Nickname: rby
Registered: Apr, 2004

Re: Ruby with Type Inference Posted: Mar 12, 2008 12:33 AM
Reply to this message Reply
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 ..)

Flat View: This topic has 2 replies on 1 page
Topic: RadRails 1.0 Supports Rails 2.0, Profiling, Rails Shell Previous Topic   Next Topic Topic: Zed Shaw on using Ruby in the Enterprise

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use