Everything is an object, even 1 and nil¶
Level: 101 · a Python programmer's first day in Ruby
One line: Every Ruby value — 1, nil, true, a string, a class, Class itself, even the top-level program — has a class, answers methods and sits on an ancestor chain that ends in BasicObject, and every operator is a named method you can call as 1.+(2).
Python says the same thing about itself, and it is nearly as true there: 1, None and int are all objects, type(type) is type, and 1 + 2 runs int.__add__. The difference is how far Ruby takes it. A literal is a receiver (3.times, "a".upcase, 1.class with no parentheses); an operator is an ordinary method with a name, so 1.+(2) and 1.send(:+, 2) are the very call that 1 + 2 makes; nil is a real object with useful methods (nil.to_a is []); and the program's top level is an object that calls itself main. The program prints each of these as a numbered row, and the Python twin further down prints the same rows in the same order.
Verified output of everything_is_an_object_rb.rb — regenerated by tools/run_examples.py, never hand-typed.
1. every value has a class
1.class Integer
nil.class NilClass
true.class TrueClass
Integer.class Class
Class.class Class
2. an operator is a method call
1 + 2 3
1.+(2) 3
1.send(:+, 2) 3
3. nil is an object with methods
nil.to_a []
nil.to_s ""
nil.inspect "nil"
4. a literal is a receiver
3.times.to_a [0, 1, 2]
"a".upcase "A"
1.class (no parentheses) Integer
5. the ancestor chain
Integer.ancestors [Integer, Numeric, Comparable, Object, Kernel, BasicObject]
NilClass.ancestors [NilClass, Object, Kernel, BasicObject]
6. everything is an Object, classes included
1.is_a?(Object) true
nil.is_a?(Object) true
Integer.is_a?(Object) true
Integer.is_a?(Class) true
7. even the top level is an object
self.inspect "main"
self.class Object
8. what a small integer cannot do
1.singleton_class TypeError
Reading the output¶
- Row 1.
1.classisInteger,nil.classisNilClass,true.classisTrueClass. A class is an object of classClass, and so isClassitself — the chain of "what is the class of that?" closes on itself. - Row 2. Three spellings of one call.
1 + 2is syntax for1.+(2), andsend(:+, 2)names the method with a symbol. That is why operators are methods you can define on your own class. - Row 3.
nilanswersto_awith[]andto_swith"", which is whynil.to_a.each { }and"#{nil}"are safe where a PythonNonewould raise. - Row 4. A literal is a receiver.
1.classparses with no parentheses because a.followed by a name after an integer literal starts a method call, not a decimal fraction. - Row 5. The chain:
Integerinherits fromNumeric, mixes inComparable, then comesObject, theKernelmodule, andBasicObjectat the root.NilClassgoes straight toObject.Kernelis whereputsandplive (Not machine-checked here — theBasicObjectpage measures it.). - Row 6.
is_a?(Object)is true for a number, forniland for the classInteger, which is itself aClass. - Row 7.
selfat the top level is anObjectthat inspects asmain, so even a script with no class in it is running inside an object. - Row 8. The one thing an
Integercannot do:1.singleton_classraisesTypeError. A small integer has no singleton class, so it cannot carry methods of its own the way a string or an array can — Ruby stores small integers, symbols,nil,trueandfalseas immediate values rather than heap objects (Not machine-checked here.).
Compared with Python¶
Python's object model is uniform in the same way, and five of the eight rows print the same answer: type(1) is int, type(None) is NoneType, type(int) is type and type(type) is type (row 1); (1).__add__(2) is the call that 1 + 2 makes and getattr(1, "__add__")(2) plays the part of send (row 2); isinstance(int, object) is True (row 6); and an int cannot carry its own attributes either — x.tag = "mine" on an integer is an AttributeError (row 8). Three rows differ. None has no to_a: list(None) is a TypeError, and str(None) is 'None' where nil.to_s is "" (row 3). A literal can be a receiver, but 1.__class__ does not even compile — the tokenizer reads 1. as the start of a float (Not machine-checked here.) — so Python needs (1).__class__, and (3).times is an AttributeError because repeating three times is for _ in range(3), not a method on the number (row 4). The ancestor chain is two long, int then object (row 5), and the top level is a module object named __main__ rather than an instance called main (row 7).
Verified output of everything_is_an_object_py.py — regenerated by tools/run_examples.py, never hand-typed.
1. every value has a class
type(1).__name__ int
type(None).__name__ NoneType
type(True).__name__ bool
type(int).__name__ type
type(type).__name__ type
2. an operator is a method call
1 + 2 3
(1).__add__(2) 3
getattr(1, "__add__")(2) 3
3. None is an object with methods
list(None) (no nil.to_a) TypeError
str(None) 'None'
repr(None) 'None'
4. a literal is a receiver
(3).times (no counterpart) AttributeError
"a".upper() 'A'
1.__class__ (compiled) SyntaxError
(1).__class__.__name__ 'int'
5. the ancestor chain
int.__mro__ names ['int', 'object']
type(None).__mro__ names ['NoneType', 'object']
6. everything is an object, classes included
isinstance(1, object) True
isinstance(None, object) True
isinstance(int, object) True
isinstance(int, type) True
7. even the top level is an object
__name__ '__main__'
type(sys.modules[__name__]) module
8. what a small integer cannot do
x = 1; x.tag = 'mine' AttributeError
The Python library's crosswalk ↗ puts every idea of that library in one table with its spellings; the object model above is what the Ruby column of such a table rests on.
Try it¶
- Write
describe(x)that prints the class ofxand its ancestors up to and includingObject, and call it on1,nil,"a",[]andInteger. Which of the five chains include a module rather than a class? 1.method(:+)returns aMethodobject. Print itsownerandarity, then do the same for"a".method(:upcase)and[].method(:each). Finally printmethod(:puts).ownerat the top level — which module doesputscome from, and where does that module sit in row 5's chain?
Solution to 1
everything_is_an_object_kata_rb.rb in full — pasted here by tools/run_examples.py from the file CI runs.
# Kata: describe(x) prints the class of x and its ancestors up to Object, so a
# reader can see that a literal, nil, a string, an array and a class all sit on
# the same kind of chain.
def describe(x)
chain = x.class.ancestors.take_while { |m| m != Kernel }
puts " #{x.inspect.ljust(8)} #{x.class.to_s.ljust(9)} #{chain.join(' > ')}"
end
puts "value class ancestors up to Object"
[1, nil, "a", [], Integer].each { |x| describe(x) }
Verified output of everything_is_an_object_kata_rb.rb — regenerated by tools/run_examples.py, never hand-typed.
See also¶
- Only
nilandfalseare falsy —nilis an object with methods, and one of only two falsy values - Duck typing asks
respond_to?, notclass— what to do with an object once you know it answers methods - Operators are methods — row 2 taken further: defining
+,[]and<=>on your own class BasicObjectandKernel— the two ends of row 5's chain, measured- Python crosswalk ↗ — the Python library's one-table map of its ideas
- Object ↗ and BasicObject ↗ — the Ruby 4.0 docs for the two classes every object inherits from
sendreaches private methods;public_senddoes not —obj.send(:name, *args)calls a method by name and ignores visibility,public_sendhonours it, and…- Twenty-three Python traps for a Ruby programmer — The twenty-three places where a Ruby reflex gives the wrong answer in Python