2.2 Day 1: Finding a Nanny

All magic aside, Mary Poppins is first and foremost a great nanny. When you first learn a language, your job is to learn how to use it to do the jobs you already know how to do. Treat this first conversation with Ruby as a dialogue. Does the conversation flow freely, or is it unnecessarily awkward? What’s the core programming model? How does it treat types? Let’s start digging for some answers.

Lightning Tour

As promised, I’m not going to take you through an exhaustive outdated installation process, but installing Ruby is a snap. Just go to http://www.ruby-lang.org/en/downloads/, find your platform, and install Ruby 1.8.6 or newer. I am running Ruby version 1.8.7 for this chapter, and version 1.9 will have some slight differences. If you’re on Windows, there’s a one-click installer that will work, and if you’re on OS X Leopard or greater, Ruby comes with the Xcode disks.

To test your installation, just type irb. If you don’t see any errors, you’re ready to handle the rest of this chapter. If you do, don’t be shy. Very few installation problems are unique. Google will show the way.

Using Ruby with the Console

If you haven’t done so, type irb. You should see Ruby’s interactive console. You’ll type a command and get a response. Give these a try:

  >> puts 'hello, world'
  hello, world
  => nil
  >> language = 'Ruby'
  => "Ruby"
  >> puts "hello, #{language}"hello, Ruby
  => nil
  >> language = 'my Ruby'
  => "my Ruby"
  >> puts "hello, #{language}"hello, my Ruby
  => nil

If you don’t already know Ruby, this brief example gives you many clues about the language. You know that Ruby can be interpreted. In fact, Ruby is almost always interpreted, though some developers are working on virtual machines that compile Ruby to byte code as it gets executed. I didn’t declare any variables. Everything I did returned a value, even when I didn’t ask Ruby to return anything. In fact, every piece of code in Ruby returns something.

You also saw at least two types of strings. One quote around a string means the string should be interpreted literally, and two quotes leads to string evaluation. One of the things that the Ruby interpreter evaluates is string substitution. In this example, Ruby substituted the value returned by the code language into the string. Let’s keep going.

The Programming Model

One of the first questions about a language you should answer is, “What is the programming model?” It’s not always a simple answer. You’ve probably been exposed to procedural languages such as C, Fortran, or Pascal. Most of us are using object-oriented languages right now, but many of those languages have procedural elements too. For example, 4 in Java is not an object. You may have picked up this book to explore functional programming languages. Some of those languages such as Scala mix programming models by throwing in object-oriented concepts. There are dozens of other programming models as well. Stack-based languages such as PostScript or Forth use one or more stacks as a central feature of the language. Logic-based languages such as Prolog build around rules. Prototype languages like Io, Lua, and Self use the object, not the class, as the basis for object definition and even inheritance.

Ruby is a pure object-oriented language. In this chapter, you’ll see just how far Ruby takes this concept. Let’s look at some basic objects:

  >> 4
  => 4
  >> 4.class
  => Fixnum
  >> 4 + 4
  => 8
  >> 4.methods
  => ["inspect", "%", "<<", "singleton_method_added", "numerator", ...
  "*", "+", "to_i", "methods", ...
  ]

I’ve omitted some of the methods from this list, but you get the picture. Just about everything in Ruby is an object, down to each individual number. A number is an object that has a class called Fixnum, and the method called methods returns an array of methods (Ruby represents arrays in square brackets). In fact, you can call any method on an object with the dot operator.

Decisions

Programs exist to make decisions, so it stands to reason that the way a language makes decisions is a central concept that shapes the way you code, and think, in a given language. Ruby is like most object-oriented and procedural languages in many ways. Check out these expressions:

  >> x = 4
  => 4
  >> x < 5
  => true
  >> x <= 4
  => true
  >> x > 4
  => false
  >> false.class
  => FalseClass
  >> true.class
  => TrueClass

So, Ruby has expressions that evaluate to true or false. True to form, true and false are also first-class objects. You can conditionally execute code with them:

  >> x = 4
  => 4
  >> puts 'This appears to be false.' unless x == 4
  => nil
  >> puts 'This appears to be true.' if x == 4
  This appears to be true.
  => nil
  >> if x == 4
  >> puts 'This appears to be true.'
  >> end
  This appears to be true.
  => nil
  >> unless x == 4
  >> puts 'This appears to be false.'
  >> else
  >> puts 'This appears to be true.'
  >> end
  This appears to be true.
  => nil
  >> puts 'This appears to be true.' if not true
  => nil
  >> puts 'This appears to be true.' if !true
  => nil

I really like Ruby’s design choice for simple conditionals. You can use both block forms (if condition, statements, end) or one-line forms (statement if condition) when you’re working with if or unless. To some, the one-line version of the if is off-putting. To me, it allows you to express a single thought in a line of code:

  order.calculate_tax unless order.nil?

Sure, you can express the previous in a block, but you would add additional noise to what should be a single, coherent thought. When you can distill a simple idea into one line, you make reading your code less of a burden. I also like the idea of unless. You could express the same idea with not or !, but unless expresses the idea much better.

while and until are similar:

  >> x = x + 1 while x < 10
  => nil
  >> x
  => 10
  >> x = x - 1 until x == 1
  => nil
  >> x
  => 1
  >> while x < 10
  >> x = x + 1
  >> puts x
  >> end
  2
  3
  4
  5
  6
  7
  8
  9
  10
  => nil

Notice that = is for assignment and == tests for equality. In Ruby, each object will have its notion of equality. Numbers are equal if their values are equal.

You can use values other than true and false as expressions too:

  >> puts 'This appears to be true.' if 1
  This appears to be true.
  => nil
  >> puts 'This appears to be true.' if 'random string'
  (irb):31: warning: string literal in condition
  This appears to be true.
  => nil
  >> puts 'This appears to be true.' if 0
  This appears to be true.
  => nil
  >> puts 'This appears to be true.' if true
  This appears to be true.
  => nil
  >> puts 'This appears to be true.' if false
  => nil
  >> puts 'This appears to be true.' if nil
  => nil

So, everything but nil and false evaluate to true. C and C++ programmers, take note. 0 is true!

Logical operators work like they do in C, C++, C#, and Java, with a few minor exceptions. and (alternatively &&) is a logical and. or (alternatively ||) is a logical or. With these tests, the interpreter will execute code only until the value of the test is clear. Use & or | to compare while executing the whole expression. Here are these concepts in action:

  >> true and false
  => false
  >> true or false
  => true
  >> false && false
  => false
 
  >> true && this_will_cause_an_error
  NameError: undefined local variable or method `this_will_cause_an_error'
  for main:Object
  from (irb):59
  >> false && this_will_not_cause_an_error
  => false
  >> true or this_will_not_cause_an_error
  => true
  >> true || this_will_not_cause_an_error
  => true
  >> true | this_will_cause_an_error
  NameError: undefined local variable or method `this_will_cause_an_error'
  for main:Object
  from (irb):2
  from :0
  >> true | false
  => true

There’s no magic here. You’ll normally use the short-circuit version of these commands.

Duck Typing

Let’s get into Ruby’s typing model a little. The first thing you need to know is how much protection Ruby will give you when you make a mistake with types. We’re talking about type safety. Strongly typed languages check types for certain operations and check the types before you can do any damage. This check can happen when you present the code to an interpreter or a compiler or when you execute it. Check out this code:

  >> 4 + 'four'
  TypeError: String can't be coerced into Fixnum
  from (irb):51:in `+'
  from (irb):51
 
  >> 4.class
  => Fixnum
  >> (4.0).class
  => Float
 
  >> 4 + 4.0
  => 8.0

So, Ruby is strongly typed,[3] meaning you’ll get an error when types collide. Ruby makes these type checks at run time, not compile time. I’m going to show you how to define a function a little before I normally would to prove the point. The keyword def defines a function but doesn’t execute it. Enter this code:

  >> def add_them_up
  >> 4 + 'four'
  >> end
  => nil
  >> add_them_up
  TypeError: String can't be coerced into Fixnum
  from (irb):56:in `+'
  from (irb):56:in `add_them_up'
  from (irb):58

So, Ruby does not do type checking until you actually try to execute code. This concept is called dynamic typing. There are disadvantages: you can’t catch as many errors as you can with static typing because compilers and tools can catch more errors with a statically typed system. But Ruby’s type system also has several potential advantages. Your classes don’t have to inherit from the same parent to be used in the same way:

  >> i = 0
  => 0
  >> a = ['100', 100.0]
  => ['100', 100.0]
  >> while i < 2
  >> puts a[i].to_i
  >> i = i + 1
  >> end
  100
  100

You just saw duck typing in action. The first element of the array is a String, and the second is a Float. The same code converts each to an integer via to_i. Duck typing doesn’t care what the underlying type might be. If it walks like a duck and quacks like a duck, it’s a duck. In this case, the quack method is to_i.

Duck typing is extremely important when it comes to clean object-oriented design. An important tenet of design philosophy is to code to interfaces rather than implementations. If you’re using duck typing, this philosophy is easy to support with very little added ceremony. If an object has push and pop methods, you can treat it like a stack. If it doesn’t, you can’t.

What We Learned in Day 1

So far, you’ve just waded through the basics. It’s an interpreted object-oriented language. Just about everything is an object, and it’s easy to get at any object’s parts, like the methods and the class. Ruby is duck typed, and Ruby behaves mostly like a strongly typed language, though some academics would argue with that distinction. It’s a free-spirited language that will let you do just about anything, including changing core classes like NilClass and String. Now let me turn you loose for a little self-study.

Day 1 Self-Study

So, you’ve had your first date with Ruby. Now, it’s time to write a little code. In this session, you’re not going to write whole programs. Instead, you’ll use irb to execute a few Ruby snippets. Try your best to work each of the problems before moving on.

Find:

Do: