Monday, July 24, 2006

From Java to Ruby ? Now ? Naah ..

Bruce Tate has recently got his From Java to Ruby out. This is another one in series of those publications which professes Ruby as the successor of Java in the enterprise. The book is targeted towards the technical managers who can stand by the decision of their enlightened programmers of making the royal switch in the enterprise and take this decision upwards within the organization (without getting fired !). I have not yet read Tate's book, but I thoroughly enjoyed reading his Beyond Java. This entry is the blueprint of my thoughts on this subject - will Java be replaced by Ruby in the enterprise today ?

Cedric has already voiced his opinion on this subject in one of his usual opinionated (Rails is also opinionated - right ?) posts. I think I have a couple to add to his list ..

Scaling Up with Ruby

One of the major areas of my concern with Ruby being mainstream is the skillset scalability of the enterprise. The programming force, at large, is now baked in the realm of the supposedly (im)pure OO paradigms of Java. Call it the Perils of Java Schools, the lack of appreciation for the elegance of functional programming or whatever, the fact is that the zillions of developers today are used to program with assignments and iterators, as they are idiomed in Java and C++. It will take quite a beating to infuse into them Why FP Matters.

Ruby is elegant, Ruby blocks are cool, Ruby has Continuations and Ruby offers coroutine based solution for the same-fringe problem. But, again, there ain't no such thing as a free lunch ! You have to develop your workforce to take good care of this elegance in developing enterprise scale applications. The following is an example of the paradigm shift, shamelessly ripped from Bruce Tate's Beyond Java :

The developers in my workforce are used to writing JDBC-style access in Spring using anonymous inner classes :

JdbcTemplate template = new JdbcTemplate(dataSource);
final List names = new LinkedList();

template.query("select user.name from user",
    new RowCallbackHandler() {
      public void processRow(ResultSet rs) throws SQLException {
        names.add(rs.getString(1));
      }
    }
);


Here is a Ruby snippet implementing similar functionality through "blocks" ..

dbh.select_all("select name, category from animal") do |row|
    names << row[0]
end


A real gem - but the developers have to get used to this entirely new paradigm. It is not only a syntactical change, it implies a new thought process on part of the developer. Remember, one of the reasons why Java could smartly rip apart the C++ community was that it was a look-alike language with a cleaner memory model and a closer affiliation to the Internet. At one point in time, we all thought that SmallTalk had an equal chance of gobbling up the C++ programming fraternity. Smalltalk is a much purer OO language, but proved to be too elegant to be adopted en-masse.

Martin Fowler and Bruce Tate have been evangelizing Ruby, DHH has presented us with a masterfully elegant framework (ROR). But we need more resources to scale up - more books, more tutorials, more evangelism on the idioms of Ruby, which have gone behind the mastery of ROR.

The Art of Ruby Metaprogramming

Metaprogramming is the second habit of Ruby programmers (possibly after "Ruby Blocks"). Many of the problems that we face today due to the lack of formal AOP in Ruby can be addressed by metaprogramming principles. In fact, metaprogramming offers much more "raw" power than AOP, as can be very well illustrated by the following method from Rails validation ..



def validates_presence_of(*attr_names)
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:blank], :on => :save }
  configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)

  # can't use validates_each here, because it cannot cope with nonexistent attributes,
  # while errors.add_on_empty can
  attr_names.each do |attr_name|
    send(validation_method(configuration[:on])) do |record|
      unless configuration[:if] and not evaluate_condition(configuration[:if], record)
        record.errors.add_on_blank(attr_name,configuration[:message])
      end
    end
  end
end



But this also reflects upon my earlier concern - the programmers have to be developed to cope with this kind of semantics in their programming. Many of the metaprogramming techniques have become idioms in Ruby - we need to have more preachings, professing their uses and best practices to the programming community. Otherwise Ruby Metaprogramming will remain a black magic for ever.

Final Thoughts

Rails may be the killer app, metaprogramming may be the killer technique, but we all need to be more pragmatic about Ruby's chance in the enterprise. There are performance concerns for Rails, the model that it adopts for ORM is divergent from the one that we do for Java and definitely not the one that can back up a solid object oriented domain model. It is debatable whether this will be a better fit for enterprise applications - but the community needs to tune the framework constantly if it were to compete with the ageold competencies of Java. With Java 5, we have a JVM which has been tuned for the last 10 years, we have killer libraries for concurrency (I heard it is capable of competing with raw C!) and we have oodles of goodies to make Java programming compete with the best of the breed performant systems. We have Mustang and Dolphin ready to make their impact on the enterprise world. It is definitely worth looking forward to whether the elegance of Ruby can scale up to the realities and give Sun (and the entire Java community) a run for their money.

4 comments:

Anonymous said...

The equivalent Java snippet to your Ruby data access snippet would be:

String query = "...";
for (Row r: dbh.execQuery(query)) {
names.add(r.get(0));
}

which is just as concise. The Spring sample is not the Java equivalent of the Ruby code shown in your blog. Having said that, Ruby is certainly a LOC-saver in some areas.

Unknown said...

[behrang said : equivalent Java snippet ..] : Actually I did not claim the Ruby snippet as equivalent to the Java snippet. I wanted to show how similar database access is handled in Ruby. In fact the entire example is from Tate's Beyond Java (as I have acknowledged).

Anonymous said...

Tate and RMH are cool guys but they are spreading FUD and are noisy as well ;)

stilet said...

hello,
I have to do 1 programm, can you check my programm
Create the abstract class Bird, there is no abstact method flapwings and String name.
Create derived abstract classes Flyingbird, Swimmingbird, Flyingandswimmingbird with corresponding abstact methods swim and/or fly.
Create no abstact subclasses from class Bird, for example Dove, Eagle, Ostrich, Swan.
Create 4 classes, which model next bird flocks:
1. flock of any birds
2. flock of flying birds
3. flock of swimming birds
4. flock of flying and swimming birds
Flocks create with the objects type Collection. There is espessial method for adding birds to the flock, it being known that adding alien objects must be impossible.
Classes structure must be so, that swan can adding to every flock. In every flock must be method, which make all birds, included in this flock, do, that all birds of flock ought do: flapwings and/or fly and/or swim.
About all actions print corresponding messages.
In test programm creat 4 different flocks, include in these flocks a few birds.
Class hierarchy must be such, that it would possible to make objects array, who can, for example to swim.

package bird;
import java.util.ArrayList;
import java.util.Iterator;
public abstract class Bird {

public String name;
public void strikewinds() {
System.out.println("Yes, relax, I'm bird and striking winds");
}

}
public abstract class Flyingbird extends Bird {
public abstract void fly();
}
public abstract class Swimmingbird extends Bird {
public abstract void swim();
}
public abstract class Flyswimbird extends Bird {

public abstract void flyswim();
}

public class Eagle extends Flyingbird {
public void fly(){
System.out.println("Yes, relax, I'm bird and fly");
}

}
public class Dove extends Flyingbird {
public void fly(){
System.out.println("Yes, relax, I'm bird and fly");
}

}

public class Penguin extends Swimmingbird {
public void swim(){
System.out.println("Yes, relax, I'm bird and swim");
}
}
public class Swan extends Flyswimbird {
public void flyswim(){
System.out.println("Yes, relax, I'm bird and fly and swim");
}

}
public class Ostrich extends Bird {

}
public class Chiken extends Bird {

}
public class Duck extends Flyswimbird {
public void flyswim(){
System.out.println("Yes, relax, I'm bird and fly and swim");
}
}

public class Arrays extends Flyingbird {
public static void main(String[] args) {
Flyingbird[] flyer = { new Eagle(), new Dove()};
for (int i = 0; i < flyer.length; i++)
flyer[i].fly();

}
}
class FlyingFlock {
private List Flyingbird birds = new ArrayList Flyingbird();

public void addBird(Flyingbird bird) {
birds.add(bird);
}

public void fly() {
}
}
class SwimmingFlock {
private List Swimmingbird birds = new ArrayList Swimmingbird();

public void addBird(Swimmingbird bird) {
birds.add(bird);
}

public void swim() {
}
}

public class Test {
public static void main(String[] args) {
ArrayList Bird flock = new ArrayList Bird();
flock.add(new Eagle());
flock.add(new Penguin());
flock.add(new Swan());
Iterator i = flock.iterator();
while (i.hasNext()) {
try {
((Flyingbird)i.next()).fly();
((Swimmingbird)i.next()).swim();
((Flyswimbird)i.next()).flyswim();

}
catch (InterruptedException e) {

}
}
}
}