What I Learned From Java That Makes Me a Better Programmer When I Use Ruby (Yes, Really)

Posted January 2, 2008

Kudos to Reg Braithwaite for the prompt for this post.

My first language, programming-wise, was Java. I’m not counting Visual Basic, which I spent about a day on before giving up in disgust at the form editor, nor whatever crazy language ran on my old Casio calculator that I never really figured out. I learned to code with Java.

A lot of people have a lot of different things to say about Java, both as a language in general and as a tool for learning. Many people – in particular, people who use languages like Ruby, Python, Lisp, Haskell, and so forth - don’t have a very high opinion of it as a language.

I find I don’t really disagree with these people. After learning Ruby, various functional languages, and even C, I’d rather program in any of those than Java.

Java seems to strike exactly the wrong balance between power and safety. It’s statically typed, but in the most verbose and unweildy way possible. You don’t get access to the bare metal like you do in C, but you also don’t get access to the higher-level abstractions found in functional languages. It doesn’t even have closures, for crying out loud.

Similarly, these people are generally taken aback by the proliferation of Java as a teaching language in universities. This is the case at the University of Washington, for instance, which is where I learned it. Joel Spolsky wrote an excellent article to this effect almost exactly three years ago.

I disagree with this sentiment. I learned a lot from Java, and I’m reasonably certain that it was as good or better a tool for learning than Python or Ruby would have been.

Now, Joel wasn’t really talking about how much a student learns from Java. He was talking about how much they’re challenged. Although the two are undoubtedly related, I’m not concerned in this post with the aspect of weeding out people who aren’t going to be able to do well in computer science.

What I’m really concerned about is people like me. If you’ll allow me a bit of immodesty, I’ll claim that I am able to do well in computer science. I have friends who certainly are. I’m interested in how it’s best to teach programming to people like that.

And I’m pretty sure Java is a good way.

The very things that make Java tedious and annoying to use in real life make it good for teaching introductory concepts. It’s an incredibly regular language. Not in the sense that it’s recognizable by a deterministic finite automaton; in the sense that it builds everything on top of very few basic abstractions.

The thing that makes Java such a good first language is that the particular abstractions it provides are the ones that underlie the higher-level abstractions that are provided by higher-level languages1.

It’s hard to grasp abstractions if you don’t understand what they’re abstracting away from. The most basic abstractions Java uses – variables and static methods - are a good basis because they correspond roughly to intuitions developed from algebra and other areas of human experience.

Once you understand these basic concepts, it’s easy to make the jump to, say, anonymous functions. They’re the same as normal functions, but they’re declared in the code. Then closures are just another step away - anonymous functions that can use variables from outside their scope.

But it’s hard to just jump in and understand closures. This is why, as much as I love Ruby, I’m very hesitant to reccomend it as a first language. You can’t program in Ruby without using blocks. It’s nearly impossible to write a loop in Ruby without explicitly or implicitly using a closure.

Java, on the other hand, must provide facilities to write a loop without closures, because it doesn’t have closures. In addition, it has to do it in the cleanest way possible, to make it nicer for Java programmers. As an example, here’s code for printing the numbers from one to ten in Ruby without using a block:

i = 1
while (i <= 10)
  puts(i)
  i += 1
end

And here it is in Java:

for (int i = 1; i <= 10; i++) {
  System.out.println(i);
}

Not only is it much cleaner in Java, but it’s idiomatic. That’s terrible Ruby style, but exactly how any Java programmer would do it.

It goes the other direction, too. Knowing Java helped me make sense of lower-level languages like C.

All through my intro programming courses, I’d heard horror stories about pointers. But when I finally learned C, they seemed perfectly straightforward.

The reason for this is that I had already been introduced to the basics of indirection when dealing with the distinction between objects and values in Java. The same metaphor my intro CS instructor used to explain objects-as-references2 worked for pointers. I think it would have been much harder to wrap my head around if I had learned the two the other way around.

So what exactly is it that I learned from Java that makes me a better programmer when I use Ruby? There are two main things.

First, I learned the basics of how to program. What variables and methods/functions are, object-orientation, data structures, and so forth. This is, of course, incredibly valuable. I couldn’t code if I didn’t know how to code.

Second, I learned what I was abstracting. I learned what blocks are, why dynamic typing is useful, what it means to redefine an operator. And I learned it from Java, by doing without.

Update: Fixed a bug in my Java loop.

1 This is true at least for imperative languages. I think it applies to functional languages, too, but to a lesser extent.

2 The metaphor goes like so: the variables are really just the phone numbers of the object. If you pass a variable to a method, you’re giving the new method the object’s phone number, not the object itself. Both phone numbers call up the same object.

Hampton said January 02, 2008:

I very, very much agree.

Java is why I’m good at Ruby.

Ikai Lan said January 02, 2008:

My first language was Basic when I was a kid, then C/C++ in high school. Yeah, I protested against Java in College, but I didn’t know what I was talking about. I’d disagree with your comparison between Java and C – the low level power in C is too tempting to a lot of programmers who will use it as an excuse not to produce good, OOP code.

That being said, I agree with your sentiment. I would even take it a step further – all the design pattern implementations in Java are trivial in Ruby (this is demonstrated pretty well in Olsen’s Design Patterns in Ruby), because of things like code blocks, meta programming, and duck typing. GoF patterns that were cumbersome in Java, like Iterators, Observers, and Strategy are trivial in Ruby, arguably not even necessary. At an even higher level, Interfaces in Java are completely unnecessary in Ruby (though sometimes I miss them). Learning and doing projects the correct way in Java makes you a better developer in general, and those skills definitely trickle down to Ruby.

Evan Farrar said January 02, 2008:

Java most definitely taught me OOP and data structures, abstraction/information hiding and more. Those concepts were the tools that allowed me to write significantly complex software instead of one-off hacks.

But I think my class in functional programming was equally important. I never use for-loops. I make a subclass maybe once a week. I can only go so many lines without a closure or higher order function.

Anonymous said January 02, 2008:
what about:
for i in 1..0
  puts i
end
Anonymous said January 03, 2008:

your java code prints from 0 to 10, not 1 to ten like you said.

Anonymous said January 03, 2008:

In ruby you can omit parens so instead of puts(i) a rubyista would write puts i (and use the shorter for loop, if he cant use a block. But if he is allowed to do so, he would probably use 0.upto(10) {|i| puts i }

Anonymous said January 03, 2008:

You suck.

Anonymous said January 03, 2008:
I think your Ruby version misses an important point: iterators are part of classes, so here I’d say (going from 0 to 10):
10.times { |i| puts(i) }
or (going from 1 to 10):
1.upto(10) { |i| puts(i) }

No ’ ++ ’ or ’ += ’, and good scoping like Java.

(Note: I just started Ruby 2 days ago)

Jeremiah said January 03, 2008:

“It’s hard to grasp abstractions if you don’t understand what they’re abstracting away from.”

I agree with this sentiment, however I don’t think Java is a very good language to learn this from. Although I’m sure that this is partly just my personal experience, I’ve found that understanding what you’re abstracting comes from a variety of sources, and you don’t really understand it until you’ve worked with a variety of languages and means of abstraction.

I mean, I seem to feel like I understand things about comp-sci in an entirely different light every few months as I learn more languages, and more means of expressing concepts through those languages.

Also, learning abstraction (and what you’re abstracting) can be totally agnostic of language. SICP is rather good for this – and while it’s presented in scheme, you could do most of it in just about any other language.

As a last point, it could be argued that you have no idea what you’re abstracting until you’ve entirely learned your way around the assembly language for your architecture of choice. Then it could be argued that you still don’t know until you’ve built a machine from the ground up.

But whatever. Part of the reason I don’t feel that Java is a good language for this is that there’s so much necessary boilerplate for accomplishing anything – and it’s not boilerplate that’s directly related to abstractions in general, it’s boilerplate thats related to Java, and getting things done in Java.

Glad it worked for you though.

sonictooth said January 03, 2008:

To last anonymous: you are using a block with both times and upto.

To the for i in 1..10 anonymous:

for … in is just syntactic sugar for the ‘each’ method, so you’re really using a block as well.

What i don’t understand is why doesn’t Nathan want us to use blocks. It doesn’t make sense to me.

It’s as if my first language was C and i asked a Java programmer to print all numbers from 1 to 10 without using objects.

Anonymous said January 03, 2008:

You do realize that the Java for-loop is an abstraction of the while-loop with an external variable, right? By that token, Ruby is closer to “bare metal” than Java, because it doesn’t add syntactic sugar like a for-loop.

Dan Bernier said January 03, 2008:

GoF patterns that were cumbersome in Java, like Iterators, Observers, and Strategy are trivial in Ruby…

Part of the reason I don’t feel that Java is a good language for this is that there’s so much necessary boilerplate for accomplishing anything – and it’s not boilerplate that’s directly related to abstractions in general, it’s boilerplate thats related to Java, and getting things done in Java.

These two comments point out one reason Java’s not a good teaching language…students spend more time learning the accidental complexity than the essential concepts. Giles Bowkett even has an anecdote about a student who learned Scheme, struggling with System.out.println().

I think you’re saying that we need an intermediate step between trivial stuff and powerful abstractions, and I agree with that. But I think a language with an interpreter and a REPL, and minimal boilerplate, is a better teaching language, because it cuts out the distractions.

What i don’t understand is why doesn’t Nathan want us to use blocks. It doesn’t make sense to me.

Nathan can correct me, but I think his point is that blocks are closures, which are function variables, which are a tough concept for beginning students. There’s plenty you can learn before you tackle that, so let’s delay it a bit. If you teach Programming 101 with Ruby, you don’t need to use blocks right away, necessarily…you don’t need to teach idioms that early.

Anonymous said January 03, 2008:

java … better programmer … ruby, puh-lease! what made me a better programmer was understanding lisp and then learning haskell.

choice quote: “Once you understand these basic concepts, it’s easy to make the jump to, say, anonymous functions. They’re the same as normal functions, but they’re declared in the code. Then closures are just another step away – anonymous functions that can use variables from outside their scope.” LOL

http://haskell.org/haskellwiki/Meta-tutorial

Anonymous said January 03, 2008:

It’s nearly impossible to write a loop in Ruby without explicitly or implicitly using a closure.

It’s also completely unnecessary to understand what a closure is in ruby to understand the syntax of the loop. Your argument is a strawman at best.

chuck said January 03, 2008:

Given these arguments, why not go back to teaching CS I in Pascal? (I’m only half kidding)

Georgi said January 04, 2008:

Hmmm… interresting.

Background on me: I learned programming using Basic, then Assembler. There was nothing else (cheaply) available then. Ventured into C, Pascal, some other languages and learned SML (at that time the “successor” of Lisp) while studying computer sience. Got into Object Pascal, C++ etc. blabla. In 1996 I postulated: “Java? Never! Do not want to learn a new language anymore.” Boy was that childish.

I read the article at joelonsoftware long ago and, mainly, I agree. You have to learn memory management (perhaps by programming C), you have to understand recursive programming. Only this way, I think, you may get a good programmer. You need to understand the concepts behind the programming language. Meaning the machine itself (simple commands, jumps, memory management …) and the abstraction in programming (recursion, functional programming, oo, closures …).

When you learn how to program verbosity is a must-have on the one side. I do not want a student writing lines like “5.times” without him understanding that “5” is an object, not a number, at that place. Taking the short cut like this perhaps is nice on a daily programming base but not so nice if people do not exactly know what they are exactly doing there. They’re sometimes just pasting some code they saw somewhere. Java is verbose enough to think of what you’re doing, that’s a little bit an object oriented programming language that might follow Pascal (in terms of learning oo-principles in Java like structured programming in Pascal).

It cannot teach you to learn how to avoid memory leaks or how to program recursive algorithms. To do so, mostly, depends on the teacher. Did I mention that one of my final tests was writing a recursive sort-algorithm for a B+-tree in Assembler? Pain in the ass – but I learned much from it.

Ruby et al cannot teach that. That’s all a little bit like Perl, you can do one thing in many ways (don’t want “{”, just don’t use it, or a better example: Look at the comments above, how Ruby-people are arguing about ONE thing to do in MANY different “more-effective” ways). Many people do not understand the underlying “magic”, because often such languages are hiding them to provide a “non-verbose” or “more-simple” way to do something. That, in my opinion, is not the way.

At first, understand what you are doing, and the more simple the regular set (“language”) is, the more you can concentrate to understand concepts and complex behaviours therein.

Then accelerate.

Just my 0,02 $, Georgi

Stephan Schmidt said January 10, 2008:

a.) I’m never going back to C. Z80, 6502, 68k – any day, it was fun. C? Never. b.) LOGO is the best first language by far [1].

Peace -stephan

[1] After experiencing Basic, GFA, E, C, Pascal, Modula, Oberon, C, C++, Java, Perl, Ruby, Python, Delphi, Tcl/Tk, 6502, Z80, 68k, Fortran, Lisp/Scheme, Prolog and many more I forgot the names

Alexei Broner said July 02, 2008:

@Georgi: You said it best.

I first learned BASIC, this taught me rudimentary concepts like, variables and how computers read instructions in a series,

Then I learned assembler, because my BASIC programs would be too slow (for graphics operations), I could incorporate asm into my BASIC programs, and the two were all that I could get my hands on for free at the time.

Asm taught me just how the computer ticked and from that point on I had a much greater understanding of what I was actually doing with my programs.

Shortly after, I moved from BASIC to Pascal, and then C. Asm had taught me why BASIC was slow and how I had to move to a compiled language.

Then I learned Java. Why? because it was freely available. I’d had to pirate copies of Turbo Pascal and C (I was 12 and didn’t know about GNU/*nix yet)

Java taught me two things, object orientation, and good programming habits. Java is verbose for one reason: It makes it easy to read and therefor easy to maintain. I believe this is the number one reason why it’s standard for enterprise development. It’s much easier to go into some unfamiliar Java code and grok what’s happening than any other language that I can think of.

But Java was slow at the time, so I took my newly found OO skills and learned C++.

I found C++ to be the best balance of power and performance, but I always thought it was much ‘dirtier’ than Java; Java seemed elegant and streamlined compared to C++.

Now, thanks to JIT compiling and supercomputers the price of dust-busters, Java is fast (enough). Java is my bread and butter language. It pays the bills and gets things done quick. It takes longer to code in than many languages, but when somebody I don’t know goes to try and change the code I’ve written, they can tell what’s happening because it’s all there plain as day.

My language of choice is Ruby (well I have a strange affinity for JavaScript, but it’s probably because I don’t know Haskell), but when the interface of an object can be modified at run-time by another object that doesn’t seem to be related at all to the first, you have to rely on having a very thorough understanding, not just of the language, but of the framework you’re working in.

It’s possible to learn to program in Ruby, but until you’re DAMN good, you’ll have a hell of a time trying to modify or debug code. Ruby was a very tough pill to swallow at first. I tried, and failed, to learn it. What it took was learning functional JavaScript and learning to pass around a function like a variable, before I could understand a block/closure.

I love Ruby for the same reasons anyone might, but without the foundation I had from a long list of languages, it would have been very difficult to understand most of the features of the language. Ruby is what it is: a great scripting language. It sits on the opposite side of the see-saw as C with Java sitting in-between.

So, to get back on topic, I agree that Java is an excellent language for learning. However, I think everyone should have to learn C first, to understand Java’s purpose, and what’s actually happening when you run a program. But C is a tough pill to swallow for someone who doesn’t know simple concepts and BASIC does that best (or a more modern variant, but not MSVB). After this foundation has been laid, then learning a more powerful scripting language can be very enlightening and empowering. Ruby, Python, and Perl fit into this category, I’d call them ‘Utility Languages’

After a ‘utility language’ is learned, then I’d move on to the more heady stuff, Scheme/Lisp, Haskell, Lolcode….

Alexei Broner said August 01, 2008:

I retract my previous post. Lisp should be learned first.

http://mitpress.mit.edu/sicp/full-text/book/book.html

Nathan said August 01, 2008:

Alexei, I’m interested in what brought around that change of heart. Did you read SICP and become awed by it?

I don’t think I’ve ever met a programmer who told me their first language was Scheme via SICP. I’d love to get an impression of what learning that first would be like; I imagine it would be very different than the traditional imperative-first approach.

dawgs said May 22, 2010:

Hey man I’m at UW and I’ve had Stuart for the CSE 142/143, that guy is awesome! I don’t know you but it’s cool to see someone who started with Reges be doing well now, cheers!

Make your comments snazzy with Textile!