DEV Community

Craig Bradley
Craig Bradley

Posted on

Everything is an Object!

Everything is an object. You may have heard this phrase before, but what does it mean?

What is an object?

An object refers to a particular instance of a class with their own methods or procedures and data variables meaning that everything is an instance of a class.

Types are objects

In statically typed languages we can define variables of different types and those variables are not objects, they don't have methods and don't have any attributes. We could say that dynamically types languages define types as classes, so for example if you have a string, it's an instance of the String class.

Let's take a look at some examples in ruby:

True/False/Nil

# Define a boolean and store as a variable my_bool
my_bool = true # => true 

# Can call the class method to return the class of the object
my_bool.class # => TrueClass

# The superclass of TrueClass is Object
TrueClass.superclass # => Object

# my_bool is an object
my_bool.is_a?(Object) # => true

my_bool = false # => false
my_bool.class # => FalseClass
FalseClass.superclass # => Object
my_bool.is_a?(Object) # => true

my_nil = nil # => nil
my_nil.class # => NilClass
NilClass.superclass # => Object
my_nil.is_a?(Object) # => true

Strings

my_string = "Some string" # => "Some string"
my_string.class # => String
String.superclass # => Object
my_string.is_a?(Object) # => true

Okay, great, strings, booleans and nil are objects but what about other datatypes?

More Datatypes

# Define an array of values with different types
my_array = [1, "two", :three, { "four" => 4 }, [5]]

# List of each objects type class
classes = my_array.map(&:class) # => [Integer, String, Symbol, Hash, Array]

# Every type class eventually inherits from object
classes.map(&:superclass) # => [Numeric, Object, Object, Object, Object]

# An Integer has a superclass of Numeric, Numeric has a superclass of Object
Numeric.superclass # => Object

# All values in my_array are objects
my_array.map { |val| val.is_a?(Object) } # => [true, true, true, true, true]

So every variable that you create is an object and this probably doesn't come as a surprise. However, what about classes? Are they objects? Yes (at least in ruby).

Classes are objects

Classes in Ruby are first-class objects — each is an instance of class Class.

So let's take a look at what this means -
When we create a class in ruby we usually use the following syntax:

# Define new class Foo
class Foo
end

# Foo is an object
Foo.is_a?(Object) # => true

but what is really happening under the hood? It might make more sense if we define a class using different syntax:

# Define new class Foo
Foo = Class.new
Foo.is_a?(Object) # => true

This does exactly the same as the traditional method above does, but I think this makes it more clear as to what is happening. Whenever you create a new class, you're really creating a new instance of Class. You're creating objects. This object is then assigned to a constant as you only want one instance of it. You can see all constants by calling the constants method on Module. See below a cut down list of constants.

Module.constants # => [:String, :Float, :NilClass, :Hash, :Array,
# :Integer, :TrueClass, :FalseClass, :Numeric, :Fixnum, :Range, 
# :Foo, :BasicObject, :Object, :Module, :Class, :Symbol]

You can see that our Foo class that was created earlier has been added to this list of constants. Every constant in this list is an object.

Object Id's

Every object in ruby has a unique id and this can be accessed by calling object_id. Using this method we can see which objects have the same id's and there are also some interesting results. Constants will maintain the same id until execution is halted. There are no guarantees that constants will maintain the same id when the program is executed again. There are exceptions to this rule however, which I will discuss now.

True/False/Nil

true1 = true
true2 = true
true1.object_id # => 20
true2.object_id # => 20

false1 = false
false2 = false
false1.object_id # => 0
false2.object_id # => 0

nil1 = nil
nil1 = nil
nil1.object_id # => 8
nil2.object_id # => 8

true, false and nil are special cases and these will maintain the same object_id every time the program is executed. There is only ever one instance of each of these values.

Integers

int_a = 1
int_b = 1
int_a.object_id # => 3
int_b.object_id # => 3

int_a = 2
int_b = 2
int_a.object_id # => 5
int_b.object_id # => 5

int_a = 3
int_b = 3
int_a.object_id # => 7
int_b.object_id # => 7

Every integer in ruby has it's own unique id and this will never change. Whenever you refer to a variable of 234, you're referring to exactly the same object. You might have noticed a pattern in the object_id's for integers. If you bit shift the integer to the left by one and add one, you will get the objects id. Vice versa if you bit shift the objects id to the right, you will get the integer. I believe this is for performance reasons.

Symbols

my_sym1 = :foo
my_sym2 = :foo
my_sym1.object_id # => 1280988
my_sym2.object_id # => 1280988

Symbols are immutable. The first time you create the symbol :foo it will create a new object and store it in a list which you can access via Symbol.all_symbols. The second time, we already have a :foo symbol in the list and therefore it will reference this one. Symbols are created once and referenced multiple times, they cannot be changed.

Strings

my_string1 = "foo"
my_string2 = "foo"
my_string1.object_id # => 70093438879580
my_string2.object_id # => 70093438872080

Strings are mutable and therefore whenever you create two strings that are equal, you'll get different objects. Although we've created two variables, both with the same value "foo" they are different objects.

So everything is an object?

When we say everything is an object, the everything part is a bit vague. Methods, blocks and operators are not objects. The lines are a little blurry with methods and blocks as they can be interpreted as objects.

Methods

# Declare a new method foo
def foo
  puts "test"
end

# Get a wrapped version of foo
my_method = method(:foo)

# my_method is an instance of Method
my_method.class # => Method

# Method has a superclass of Object
Method.superclass # => Object

# Can invoke the method using call
my_method.call # => "test"

It's unclear whether methods are actually objects or not. They behave like objects but you can never access the method directly. You will always get a wrapped version of the method which acts like a closure. I would be tempted to say that methods are not objects.

Summary

To summarise, almost everything in ruby is an object. Every data type that we work with is a class and classes are objects. Even the Object class is an object. Strings, integers, floats, hashes, arrays, symbols, classes, modules, errors and more are all objects. Open up an irb console and see for yourself, there are some really interesting observations to make.

Oldest comments (7)

Collapse
 
craigbrad profile image
Craig Bradley • Edited

I'm not trying to explain the differences between statically/dynamically typed languages. This is more of a general observation and may not be true for every static/dynamic language and yes, I know Java is statically typed (Not sure what your point is here). My point was - Not everything in statically typed languages are objects, they can't be unless you remove primitives. i.e types are checked at compile time, no objects exist at compile time.

Again, Method objects represent a given method. They're some variation of a Proc that wrap the underlying implementation. This is a nicety that ruby has, allowing you to pass around a wrapped method.

 
craigbrad profile image
Craig Bradley

Ruby absolutely doesn't allow you to pass around instances of a method. A method is converted into a Method object when you call the method method.

 
craigbrad profile image
Craig Bradley

The Method object you're referring to allows you to indirectly call the method, using .call. This passes a message to the object which is where the method is executed. The Method object itself, is not the method. Yes, you will get the same object id for the Method object, but that doesn't mean it's the method itself.

Collapse
 
tadman profile image
Scott Tadman

Methods as defined on a class, module or object directly are part of that object. They don't exist independently except when referenced with things like Method. I'm not sure this is inconsistent, it's just how it's structured. "Everything is an object, or a property of an object" is a more specific rule, perhaps?

Then there's the question of what method calls are. Are those objects too? They're messages passed between objects, an approach actually different from conventional function calls. Messages can be intercepted, redirected, rewritten and so on at run-time in a very arbitrary fashion, while function calls are often compiled into code as-is, immutably.

What is 1 + 2 in Ruby? The answer is not necessarily 3.

So maybe it should be "Everything in Ruby is an object, a property of an object, or a method call on an object."

Collapse
 
craigbrad profile image
Craig Bradley • Edited

Thanks for the response. The more you dig into the language, the more interesting it gets. Messages are an interesting subject and were out of scope of this post. I suppose it’s how you interpret “Everything is an object”. I debated including methods, but was hoping someone could clear it up.

Collapse
 
cout970 profile image
cout970

I don't get why "Everything is an object" is a good thing. Value objects are really useful and they have better performance, mainly because they use the CPU cache more efficiently. Ruby doesn't have value objects and I don't think it needs them, but they are useful in other languages.

Collapse
 
craigbrad profile image
Craig Bradley

It's not necessarily a good thing, but in my opinion it's what allows me to write more expressive code quickly and easily. For example, simple checks:

In c++ to check a number is equal to zero we can do the following:

int num = 0;

if (num == 0) {
  return "match";
}

In ruby we can do it this way:

num = 0

if num.zero?
  "match"
end

I prefer the ruby way of doing this as it is much simpler to read. It's closer to English. Ruby provides hundreds of useful methods, just take a look at the Numeric class in the ruby docs.

That's not to say one is better than the other. If you're looking for performance, then you are probably not going to be using ruby anyway.