Values, and the names you give them
Ruby has no primitives: everything is an object you can ask questions of. Numbers, text, symbols, nil, and the conversions between them.
Everything is an object
In Ruby, 42 is not a special piece of hardware — it is an Integer object with methods. 42.even? asks it a question, 42.class says what it is, and 42.to_s turns it into text. The same is true of every other value, which is why the language feels so even: there is one set of rules, and everything follows them.
That makes small programs pleasant and large ones predictable in a way that mixed languages are not. A number knows arithmetic, text knows about letters, and nil — Ruby's name for the absence of a value — is an object too, with methods of its own.
Numbers and names
Integers are exact and unbounded, so 2 ** 100 is a perfectly ordinary thing to ask for. Floats are the fastest approximate type and behave the way floats do everywhere. There is no separate character type — a one-letter string is a string, which removes a whole category of confusion.
A variable is a name bound to a value: score = 10, then score = score + 5. There is no declaration and no type attached to the name — the value carries its own type, and the name can point at anything. A name that begins with an uppercase letter is a constant, and Ruby warns if you reassign one; that warning is the entire enforcement, so constants are a promise rather than a lock.
Symbols, and asking what a value is
A symbol looks like :name and is a small immutable label. Where other languages reach for an enum or a string key, Ruby uses a symbol: it compares by identity, it reads like a name, and it is the natural key of a hash — chapter seven is where that starts to matter. In the output, a symbol prints with its colon, and a string prints with quotes if you ask for its raw form with p.
Conversion is explicit: to_i turns text into an integer, to_f into a float, to_s into a string. Ruby almost never converts behind your back, which is why '40'.to_i + 2 needs the to_i and why this is a feature — you can see every step where a value changed shape.
Names point at objects
Assignment does not copy a value into a box; it points a name at an object. score = 10 makes score refer to the Integer 10, and score = score + 5 builds 15 and points the name at that instead. The old object is unchanged, which matters once two names refer to the same mutable object — the arrays in a later chapter will make that concrete.
Because the name carries no type, the useful questions are asked of the value: score.class, score.is_a?(Numeric), score.respond_to?(:even?). The last one is duck typing in a sentence — if the object answers the method, the code can call it. Ruby checks at the call rather than at the declaration, so a wrong value fails where it is used instead of where it was named.
Conversion is forgiving in a way that can hide a problem: '12abc'.to_i is 12, and 'abc'.to_i is 0, because to_i reads the digits it finds and stops. Integer('12abc') raises instead, which is the honest choice for input you do not trust. And nil.to_s is the empty string, so a missing value can look like blank text rather than a failure.
Asking values what they are
puts 42.class
puts 3.14.class
puts 'text'.class
puts :label.class
puts nil.class
puts 2 ** 100
puts (1 + 2) * 3
puts 'ruby'.upcase
puts 'ruby'.length
puts 42.even?
puts '40'.to_i + 2
puts 42.to_s + '!'
puts '3.5'.to_f * 2
score = 10
score = score + 5
puts "score: #{score}"
FULL_MARKS = 100
puts "out of #{FULL_MARKS}"
missing = nil
p missing
puts missing.to_sA small profile
Run it and read the class names. Then add a constant for the year you were born, compute an age from it with plain arithmetic, and print a sentence with that number interpolated.
A name is not a type
Because a name can point at any object, the same name can hold a string on one line and a number on the next. Ruby allows it, and the program only notices when a method is missing: calling upcase on a number raises NoMethodError, which names the class and the method it could not find.
That error is more useful than a type declaration in a small program, because it arrives with the value in hand. Read it as a question: what did this object turn out to be, and which line gave it that value? The answer is usually a conversion that was skipped.
nil is a value, not an error. It answers nil.class with NilClass, prints as an empty string under puts, and prints as nil under p — so when output looks blank where you expected something, p is how you find out whether you got an empty string or nothing at all.
Try these next
- Ask three values for
.class, then ask one of them.respond_to?(:upcase). - Convert
'3.99'to a float, double it, and print the class of the result. - Turn a number into text with
to_s, add an exclamation mark, and check the class of the result.
A price tag that adds up
Run it and check each conversion. Then change the quantity, add a discount constant, and print the final price with two decimal places.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What does 42.class return?
