|
|
Object-oriented |
Functional |
|
Statically typed |
Java/C++ |
OCaml |
|
Dynamically typed |
Ruby |
Scheme |
I said that I wanted to discuss the question of where innovation comes from in our field in terms of programming languages. In the 1970's a lot of innovation came from Alan Kay and the people who worked for him. Alan is one of the pioneers of object-oriented programming and one of the chief architects of Smalltalk. He worked for Xerox PARC during the 1970's. One of his most famous quotes is that, "The best way to predict the future is to invent it," which you'll find along with some amusing stores in Kay's paper on The Early History of Smalltalk (one of the most interesting papers I have ever read). Kay and his colleagues invented the future at Xerox Parc in the 1970's: a computer with a graphical user interface, a mouse, using a desktop metaphor, on a network with services like email and chat, programmed with object-oriented techniques. Xerox couldn't figure out how to make money from it. That took Apple and Steve Jobs. Then Microsoft made it work for IBM PCs and the rest is history.
The most recent wave of innovation came in the early 1990's. So what happened in the early 1990's? That's when Tim Berners Lee invented the world wide web and that changed everything. The big problem with the web was that it had only one-way communication. You could create web pages that would present data along with hyperlinks, but the page couldn't interact with the user.
Sun was working on Java at the time, although they were targeting it for embedded systems (programs running on handheld devices like cell phones). When the web became popular, they realized that Java would be useful for programming applets that would run in a browser. Applets have always had some degree of popularity, but they have generally turned out to be too big of a pain for people.
The other dominant paradigm that emerged was the idea of running software on a server that would interact with a user. So the challenge was to program the server. Suddenly scripting languages became very important because they allowed programmers to quickly develop code to be run on the server. This was a very different kind of programming task than writing a gigantic program like Microsoft Word. Plus, because it was running on the server, you had a great deal of flexibility about what language to use (any language that you could put on your server).
Perl was the early favorite scripting language, but many other popular languages have emerged to fill this niche. Python and Ruby are two such languages. I gave the analogy that they are to the programming world what indie films are to the movie industry. Java, C++ and C# are like Hollywood blockbusters that cost incredible amounts of money to produce. Python and Ruby are like small independent films that are produced on a tiny budget but are often more creative and interesting than the films produced by Hollywood.
Ruby is the brainchild of Yukihiro Matsumoto, also known as "Matz". He has said that one of his primary goals was to create a language that programmers would find fun to use. Given what I've seen and heard about Ruby, my opinion is that he has succeeded.
We spent most of the lecture in irb seeing how Ruby works. I won't try to recreate that in the notes, but I'll mention some of the key points to remember:
I mentioned that the four major types of data I want to look at are numbers, strings, arrays, and maps (Ruby calls them Hashes). Numbers and strings are pretty familiar. Arrays are specified using square brackets and a comma-separated list of values, as in [3, 5, 2.8, ["hello", 9], 17]. Notice that we can have arrays inside of arrays and that like lists in Scheme, arrays in Ruby are more flexible than what we get in OCaml in that we can mix different kinds of data in a single array.
Ruby is dynamically typed, which means that we don't have to describe the types of variables, parameters, and method return values. It also means that a variable can refer to different types of data at different times. Ruby is still type-safe in that it checks to make sure you don't combine values in an illegal way, although we found some pretty odd combinations that Ruby allows, as in "hello" * 3 which returns "hellohellohello" or [1, 2, 3] * ":" which returns "1:2:3".
Ruby is a pure object-oriented language. Everything is an object, even simple numbers. So we found that we could ask the number 3 what its methods are by saying 3.methods(). We also found we could eliminate the parentheses and just ask for 3.methods. Even operations like addition and multiplication are methods that have a shorthand notation. When you say "3 + 4" you are calling a method + on the object 3 passing it 4 as a parameter: 3.+(4).
We were able to ask each object what class it is by saying things like 3.class or "hello".class. This is called reflection and is an important part of modern programming languages. The ability to ask for 3.methods is another example of reflection. Java has a great deal of support for reflection through packages like java.lang.reflect, although it's much easier to do this in Ruby. We saw that we could ask any object for its methods, as in 3.methods (methods of integers) or "hello".methods (methods of strings) or 3.methods.methods (methods of arrays).
We saw that we could define new methods with def...end, as in:
def f(n)
return n * 2
end
This defines a function f that doubles a number (f(8) returns 16). This looked
like a static method in Java, but in fact it is defined in a default class
called "main". We were able to see it by asking for self.methods ("self" is
the Ruby equivalent of Java's "this" keyword).We were able to pass all sorts of values to function f:
irb(main):004:0> f(12)
=> 24
irb(main):005:0> f(3.8)
=> 7.6
irb(main):006:0> f("hello")
=> "hellohello"
irb(main):007:0> f([1, 2, 3])
=> [1, 2, 3, 1, 2, 3]
This is an example of the "duck" typing that Ruby uses. What matters to Ruby
is that the value you pass to the function is able to respond to the message "*
2". If it can do that (if it can quack that way), then it's okay (it's the
kind of duck this function is expecting).Ruby has a much more consistent syntax for structured objects.
structure # elems? elem i?
-------------------------------------------
array a.length a[i]
String a.length() a.charAt(i)
ArrayList a.size() a.get(i)
Ruby (all) x.length x[i]
We also saw that Ruby has two ways to define a "slice":
x[m, n] #n values starting at index m
x[m..n] #values between indexes m and n inclusive
Ruby also has a type called Hash that is similar to a Java Map. It uses curly
brace notation. You can either call Hash.new to construct one or refer to {}.
Once defined, you use array-like syntax, as in:
x = {}
x["hello"] = 74