Sometimes the easiest way to get a feeling for a language is to compare it to a language you already know. I will show how loops, functions, objects, and other things appear in these languages:
This is the classic program for comparing languages. Java and C++ have a lot of overhead here.
#include <iostream> int main(int argc, char * argv[]) { cout << "Hello, world" << endl; }
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, world"); } }
print "Hello world"
puts "Hello world"
C++ and Java both have primitives -- things that are not objects. Ruby is objects all the way. Python is special -- there are primitives, but the language works hard to make them look like objects.
void main() { short s = 0x7fff; unsigned short us = 0xffff; int i = 0x7ffffff; unsigned int u = 0xffffffff; long l = 0x7fffffffL; unsigned long ul = 0xffffffff; float f = 1.2E30F; double d = 1.2E30; char c = 'a'; int ia[] = {1, 2, 3}; char * p = "foo"; }
public class Primitives { short s = 0x7fff; int i = 0x7ffffff; long l = 0x7fffffffffffffffL; float f = 1.2E30F; double d = 1.2E30; char c = 'a'; char u = '\u1234'; byte b = -127; }
The languages in our little survey are all block-structured, but handle blocks differently. Python is the real oddball, with indentation used to define blocks.
#include <iostream> void foo(int i) { if (i == 0) { cout << "It's zero" << endl; cout << "Nada" << endl; cout << "Zilch" << endl; cout << "Null" << endl; } else cout << "It's not zero" << endl; }
class Blocks { public void foo(int i) { if (i == 0) { System.out.println("It's zero"); System.out.println("Nada"); System.out.println("Zilch"); System.out.println("Null"); } else System.out.println("It's not zero"); } }
def foo(i): if i == 0: print "It's zero" print "Nada" print "Zilch" print "Null" else: print "It's not zero"
def foo(i) if i == 0 puts "It's zero" puts "Nada" puts "Zilch" puts "Null" else puts "It's not zero" end end
#include <iostream> void main() { for (int i = 0; i < 10; i++) cout << i << endl; }
public class Looping { public static void main (String[] args) { for (int i = 0; i < 10; i++) System.out.println(i); } }
for i in range(0, 10): print i
10.times do |i| puts i end
Arrays in Java and C++ can't change size and can only hold one type of thing.
int main(int argc, char * argv[]) { int a[] = {1, 2, 3}; }
class Arrays { int[] a = {1, 2, 3}; }
Lists have a richer behavior than arrays. Ruby arrays go here because, even through they're called arrays, they have very rich behavior.
#include <list> void main() { list<int> l; l.push_back(0); l.push_back(1); l.push_back(2); for (list<int>::iterator i = l.begin(); i != l.end(); ++i) cout << *i << endl; }
import java.util.*; public class Lists { public static void main (String[] args) { List list = new LinkedList(); list.add(new Integer(1)); list.add("foo"); list.add(new int[] {2, 3}); for (Iterator iter = list.iterator(); iter.hasNext();) { Object o = iter.next(); System.out.println(o); } } }
a = [1, "foo", [2, 3] ] a.append("bar") for element in a: print element
a = [1, "foo", [2, 3] ] a << "bar" a.each do |element| puts element end
a = (1, "foo", [2, 3] ) #a[0] = 2 #not allowed a[2][0] = 3 #allowed
Lots to talk about here. Some string literals allow escape sequences, some don't. Some are unicode, most aren't. Some are multi-line. And some are primitive and some are objects.
#include <string> void main() { string s = "line 1\nline 2\n"; }
public class StringLiterals { public static void main (String[] args) { System.out.println("line 1\nline 2"); } }
a = "line1\nline 2" a = 'line1\nline 2' a = r"line 1\nline 2" a = """line 1 line 2"""
puts "line 1\nline 2" puts 'line 1\nline 2' puts %Q{Double quotes look like this: "} puts %q{Single quotes look like this: '} puts <<-END line 1 line 2 END
All of these languages do string concatenation in a pretty straightforward way. All but Ruby will concatenate string constants at compile time. As far as I know, Ruby always concatenates at run time (it doesn't really have a separate compile step like the other three languages).
#include <string> void main() { string a = "This and "; string b = "that"; string c = a + b; // runtime string d = "this and " "that"; // compile time }
public class StringConcatenation { String a = "This string " + " and that string. "; // compile time String b = a + a; // runtime }
print "This and " "that" # compile-time print "This and " + "that" # runtime
puts "This and " "that" puts "This and " + "that"
Here we're just printing a string and an integer together. Ruby has an interesting syntax for turning variables into strings right in the middle of a string constant. Python has something like C's printf function. Java just has string concatenation, but it will turn the variable into a string for you. C++ has stream operators, which through the magic of operator overloading turn the variables into strings for you.
#include <iostream> void main() { int i = 1; cout << "i = " << i << endl; }
public class IntegerOutput { public static void main (String[] args) { int i = 1; System.out.println("i = " + i); } }
foo = 1 print "foo=%i" % foo
i = 1 puts "i = #{i}"
File output is pretty straightforward. Java is complicated by its unicode support -- it uses a PrintWriter to map between unicode and binary, and a FileWriter to write binary to a file.
If you use the syntax shown here, Ruby closes the file for you (even if an exception occurs).
#include <fstream> void main() { ofstream f("temp1"); f << "line1" << endl; f << "line2" << endl; f.close(); }
import java.io.*; public class FileOutput { public static void main(String[] args) throws Exception { PrintWriter out = new PrintWriter(new FileWriter("temp1")); out.println("line1"); out.println("line2"); out.close(); } }
file = open("temp1", "w") file.write("line1\n") file.write("line2\n") file.close()
File.open("temp1", "w") do |file| file.puts "line1" file.puts "line2" end
Here's a simple object with an instance variable that's initialized when the object is created, and an accessor and setter for that variable.
Ruby can generate the setter and accessor for you.
#include <iostream> class Foo { private: int a; public: Foo(); int getA() { return a; } void setA(int a); }; Foo::Foo() : a(1) { } void Foo::setA(int a) { this->a = a; } void main() { Foo f; cout << f.getA() << endl; f.setA(2); cout << f.getA() << endl; }
public class Foo { private int a; public static void main (String[] args) { Foo f = new Foo(); System.out.println(f.getA()); f.setA(2); System.out.println(f.getA()); } public Foo() { a = 1; } public int getA() { return a; } public void setA(int a) { this.a = a; } }
class Foo: def __init__(self): self.a = 1 def getA(self): return self.a def setA(self, a): self.a = a f = Foo() print f.getA() f.setA(2) print f.getA()
class Foo attr :a, true #auto-generate accessor and setter for @a def initialize @a = 1 end end f = Foo.new puts f.a f.a = 2 puts f.a
All of these languages support single inheritance.
#include <iostream> class Foo { public: virtual void saySomething() { cout << "foo" << endl; } }; class Bar : public Foo { virtual void saySomething() { cout << "bar" << endl; } }; void main() { Foo * foo = new Bar(); foo->saySomething(); delete foo; }
class Foo { public void saySomething() { System.out.println("foo"); } } class Bar extends Foo { public void saySomething() { System.out.println("bar"); } } public class Inheritance { public static void main (String[] args) { Foo foo = new Bar(); foo.saySomething(); } }
class Foo: def saySomething(self): print "foo" class Bar(Foo): def saySomething(self): print "bar" foo = Bar() foo.saySomething()
class Foo def saySomething puts "foo" end end class Bar < Foo def saySomething puts "bar" end end foo = Bar.new foo.saySomething
C++ makes you manage memory explicitly (although you can use templates to do many memory management chores).
Ruby, Python, and Java are garbage collected.
class Bar { }; class Foo { private: Bar * bar; public: Foo() { bar = new Bar; } ~Foo() { delete bar; } }; void main() { Foo * foo = new Foo(); delete foo; }
class Bar { } class Foo { private Bar bar; public Foo() { bar = new Bar(); } } public class Memory { public static void main(String[] args) { Foo foo = new Foo(); } }
class Bar: pass class Foo: def __init__(self): self.bar = Bar() foo = Foo()
class Bar end class Foo def initialize @bar = Bar.new end end foo = Foo.new
Destructors are a bread-and-butter method in C++, since you have to do your own memory management. Destructors aren't often used in Python, Ruby, and Java, since those languages do memory management (and most other resource management) for you. Ruby doesn't even have destructors.
#include <iostream> class Foo { public: virtual ~Foo() { cout << "Adios" << endl; } }; void main() { delete new Foo(); }
public class Destructors { protected void finalize() { System.out.println("Adios"); } public static void main(String[] args) { new Destructors(); } }
class Foo: def __del__(self): print "Adios" Foo()
C++ exceptions are the simplest, having only "try" and "catch". They are also the most dangerous (ask me about raw pointers and C++ exceptions).
Java adds "finally".
Ruby and Python are the most complete, having "else" as well.
#include <iostream> void main() { try { throw 42; } catch(int i) { cout << "caught: " << i << endl; } }
public class Exceptions { public static void main(String[] args) { try { throw new Exception("blah"); } catch(Exception e) { System.out.println("Caught: " + e); } finally { System.out.println("Done"); } } }
import sys try: try: raise "foo" except "foo": print "Caught: " + str(sys.exc_type) else: print "No exception occured" finally: print "Done"
begin raise "foo" rescue Exception puts "Caught: " + $! else puts "No exception was thrown" ensure puts "Done" end
C++ and Python support multiple inheritance.
Ruby supports mixins, which do mostly the same thing.
Java supports multiple inheritance of interfaces only -- you can only inherit implementation (code) from one class in Java.
#include <iostream> class Foo { public: void sayFoo() { cout << "foo" << endl; } }; class Bar { public: void sayBar() { cout << "bar" << endl; } }; class Baz : public Foo, public Bar { }; void main() { Baz baz; baz.sayFoo(); baz.sayBar(); }
interface Foo { public void sayFoo(); } interface Bar { public void sayBar(); } public class Baz implements Foo, Bar { public void sayFoo() { System.out.println("foo"); } public void sayBar() { System.out.println("bar"); } public static void main(String[] args) { Baz baz = new Baz(); baz.sayFoo(); baz.sayBar(); } }
class Foo: def sayFoo(self): print "foo" class Bar: def sayBar(self): print "bar" class Baz(Foo, Bar): pass baz = Baz() baz.sayFoo() baz.sayBar()
module Foo def sayFoo puts "foo = #{@foo}" end end module Bar def sayBar puts "bar = #{@bar}" end end class Baz include Foo include Bar def initialize @foo = 1 @bar = 42 end end baz = Baz.new baz.sayFoo baz.sayBar
Ruby and Python are dynamically typed: Every object has a type, but you never declare the type of a variable.
Here we can also see how Ruby uses the value of the last statement executed as the return value of a method.
def min(a, b): if a < b: return a else: return b print min(1, 2) print min(1.0, 2.0) print min("abc", "def")
def min(a, b) if a < b a else b end end puts min(1, 2) puts min(1.0, 2.0) puts min("abc", "def")
Templates give C++ some of the goodness of dynamic typing while retaining strong typing.
Java, being strongly typed but lacking templates, is severely handicapped.
#include <iostream> #include <string> template <class T> T min (const T & a, const T & b) { if (a <= b) return a; else return b; } void main() { cout << min (1, 2) << endl; cout << min (1.0, 2.0) << endl; cout << min (string("abc"), string("def")) << endl; }
This is the ability to define an unnamed function on the fly. This is great for those little callbacks.
C++ can do this with templates or macros, but I didn't discover how to do it trivially enough to include here (people write libraries for it). Lambda functions in Java are annoyingly non-trivial, but I included Java here anyhow.
public class Lambda { interface Func { public int exec(int x); } public static void foo(Func f) { System.out.println(f.exec(4)); } public static void main(String[] args) { foo(new Func() { public int exec(int x) { return x * 2; } }); } }
def foo(f): print f(4) foo(lambda x: x * 2) #prints "8"
a = [1, 2, 3, 4, 5] print map(lambda x: x * 2, a) #[2, 4, 6, 8, 10] print filter(lambda x: x % 2 == 0, a) #[2, 4]
def foo puts yield(4) end foo { |x| x * 2} #prints "8"
Both Ruby and Python can mutate a class definition on the fly.
class Foo: def printOne(self): print 1 foo = Foo() def printTwo(self): print 2 Foo.printTwo = printTwo foo.printOne() foo.printTwo()
class Foo def printOne puts 1 end end class Foo def printTwo puts 2 end end foo = Foo.new foo.printOne foo.printTwo
Ruby can add methods to specific objects. I think Python can do that too, but I was unable to come up with the right syntax for it so I'm not sure.
Ruby has two syntaxes for this. They both work the same way.
class Foo def printOne puts 1 end end foo = Foo.new() def foo.printTwo puts 2 end foo.printOne foo.printTwo
class Foo def printOne puts 1 end end foo = Foo.new() class << foo def printTwo puts 2 end end foo.printOne foo.printTwo