Post

Why I like Ruby 2

Part one.

2. Typing system

Ruby is strongly and dynamically typed - this is sometimes called duck typing, which is not quite right, but I suppose it is close enough.

A language can be dynamic and the runtime still does type checks. Ruby does not. What it does is check to see if the method that is called for a given object exists or not.

This is the part that is called duck typing. “If it walks like a duck and quacks like a duck and looks like a duck”…

Do not mistake it for weak typing or no typing at all.

Let me repeat that. Ruby is strongly typed.

1
2
3
4
5
6
7
1 +'1'

1 + '1'.to_i

1.+(1)

array = [1,2.0,'string']

The third line shows that ‘+’ is a method on the object 1, not an operator. Operator overloading is not allowed in Ruby.

The last line demonstrates that data structures can take any type. They will sort deterministically even if every type in the collection is different.

The output for the three lines:

1
2
3
4
5
6
7
TypeError ('String cannot be coerced into Integer')

2

2

[1,2.0,'string']

An object can not ever be another type. Never ever, ever!

There is no casting. None of the casting nonsense that you see elsewhere can be done in Ruby. That LinkedList you wrote is a LinkedList even if it inherits from List. It will never just be a List.

Even if you are depraved enough to want to do that in Ruby, it won’t let you and there is no need since the language is duck typed. References can change type, but not objects, and they are not the same thing. References are nothing more than a label. Reference is an unfortunate name that many languages use, and it confuses people, especially when it comes to passing mechanisms.

Ruby References are really labels
1
2
3
4
#This is expected in dynamic languages
data = [] #it is an array
data = {} #now it is a hash
data = 1 #it is an integer

Speaking of, passing in Ruby is pass-by-value only. That is like almost every language in existence. Off the top of my head, only C++ out of the mainstream languages - is Pascal a mainstream language anymore? - support pass by reference. Anything in Ruby, Java, etc, that looks like pass-by-reference is simply the consequence of mutable objects. Here is a Java-based article that explains it properly.

If you are still confused, think of them as labels.

This typing style allows different objects to be passed to a method without issue so long as the methods on the object that are called exist.

This does require a little discipline, but it is simple once it is understood. The method ‘map()’ that supports the functional style of programming can take pretty much any data structure that includes the Enumerable module that map() belongs to.

Some folks don’t like the flexibility and freedom that it brings. It does help to simplify the code. Not having to write lots of map methods to account for all sorts of types, concrete and generic. Each with different corner cases and bugs simplifies things greatly. I will talk about it later, but blocks make methods like map() infinitely configurable.

I see Java programmers, calling some built-in reflection methods in Ruby to check for type.

That is unnecessary in Ruby, and it just complicates the code. What matters is if the method called exists on that object. If not there is a built-in way that can deal with that at runtime. ‘method_missing()’ can be used for various things and is automatically called when an object does not have the method expected. The default implementation is to raise an error that will stop the program, but it can be overridden. When overriding it, you could write a custom error handler and then decide how to proceed. It can also be used to create that wrongly called method dynamically and then call it. That is a rather advanced and obscure feature that would fit in a different article but is pretty neat and is used in some very powerful libraries.

Anyway, don’t do manual type checking, at least not without a great reason, even though there are reflection methods to do just that. Oddly, I don’t see programmers, especially coming from Java checking if a method exists on an object. That makes more sense than checking for the object type, but don’t do that either! The methods is_a? and respond_to? have their uses but not to fake something more similar to static type checking, although these checks won’t be run until runtime.

Seriously, don’t do this!
1
2
3
4
5
6
7
8
class Test
  def do_something str_val
    puts str_val.reverse if str_val.is_a? String # clean single line if statements
    if str_val.respond_to? :upcase
     puts str_val.upcase
    end
  end
end

Running the object:

1
2
3
4
5
6
7
Test.new.do_something 'string'
#Output
gnirts
STRING

Test.new.do_something :not_a_string
# It won't print anything

3. Object-Oriented all the way down

Using some reflection methods, we can use to take a peek under the hood.

Everything is an object, even a class
1
2
3
4
5
6
7
  String.is_a? Object

  #Object hierarchy for String
  String.ancestors

  #Displays all of the methods that String will respond to
  String.methods.sort
Output
1
2
3
4
5
  true

  [String, Comparable, Object, Kernel, BasicObject]

  [:!, :!=, :!~, :<, :<=, :<=>, :==, :===, :=~, :>, :>=, :__id__, :__send__, :alias_method, :allocate, :ancestors, :attr, :attr_accessor, :attr_reader, :attr_writer, :autoload, :autoload?, :class, :class_eval, :class_exec, :class_variable_defined?, :class_variable_get, :class_variable_set, :class_variables, :clone, :const_defined?, :const_get, :const_missing, :const_set, :const_source_location, :constants, :define_method, :define_singleton_method, :deprecate_constant, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :include, :include?, :included_modules, :inspect, :instance_eval, :instance_exec, :instance_method, :instance_methods, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :method, :method_defined?, :methods, :module_eval, :module_exec, :name, :new, :nil?, :object_id, :prepend, :private_class_method, :private_constant, :private_instance_methods, :private_method_defined?, :private_methods, :protected_instance_methods, :protected_method_defined?, :protected_methods, :public_class_method, :public_constant, :public_instance_method, :public_instance_methods, :public_method, :public_method_defined?, :public_methods, :public_send, :remove_class_variable, :remove_instance_variable, :remove_method, :respond_to?, :send, :singleton_class, :singleton_class?, :singleton_method, :singleton_methods, :superclass, :taint, :tainted?, :tap, :then, :to_enum, :to_s, :trust, :try_convert, :undef_method, :untaint, :untrust, :untrusted?, :yield_self]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  class Test
    attr_accessor :data

    def Test.set_var val
      @@class_var = val
    end

    def initialize data=[]
      @data = data
    end
  end

  Test.new.instance_variables

  Test.class_variables
Output
1
2
3
4
 
  [:@data]

  [:@@class_var]
Even methods are objects
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Test
  def do_something 
   puts 'working hard!'
  end
end

method = Test.new.method :do_something

puts method.is_a? Object
puts method.class

#if do_something had arguments they can be listed
puts method.parameters

#can call into the method since it is a callable
puts method.call
Output
1
2
3
4
5
6
7
true

Method

[] #no arguments return an empty array

working hard!
There is no such thing as a static class in Ruby.
1
2
3
4
5
6
7
8
#What looks like a static class is an object that is a child of Class.
class Test
end

Test.instance_of? Class

#Output
true

If writing in an imperative style, Ruby will automatically wrap everything in an object

Even when it doesn’t look object-oriented it is
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def function1 arg1
 puts self.class
end

def function2
puts self.methods.size #print the number of methods available in this object
end

#call functions
function1
function2

#output
Object
115
Numeric values are objects
1
2
3
4
5
6
7
8
42.is_a? Object
=>true

42.methods.size
=>145

42.methods.sort
=>[:!, :!=, :!~, :%, :&, :*, :**, :+, :+@, :-, :-@, :/, :<, :<<, :<=, :<=>, :==, :===, :=~, :>, :>=, :>>, :[], :^, :__id__, :__send__, :abs, :abs2, :allbits?, :angle, :anybits?, :arg, :between?, :bit_length, :ceil, :chr, :clamp, :class, :clone, :coerce, :conj, :conjugate, :define_singleton_method, :denominator, :digits, :display, :div, :divmod, :downto, :dup, :enum_for, :eql?, :equal?, :even?, :extend, :fdiv, :finite?, :floor, :freeze, :frozen?, :function1, :function2, :gcd, :gcdlcm, :hash, :i, :imag, :imaginary, :infinite?, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :integer?, :is_a?, :itself, :kind_of?, :lcm, :magnitude, :method, :methods, :modulo, :negative?, :next, :nil?, :nobits?, :nonzero?, :numerator, :object_id, :odd?, :ord, :phase, :polar, :positive?, :pow, :pred, :private_methods, :protected_methods, :public_method, :public_methods, :public_send, :quo, :rationalize, :real, :real?, :rect, :rectangular, :remainder, :remove_instance_variable, :respond_to?, :round, :send, :singleton_class, :singleton_method, :singleton_method_added, :singleton_methods, :size, :step, :succ, :taint, :tainted?, :tap, :then, :times, :to_c, :to_enum, :to_f, :to_i, :to_int, :to_r, :to_s, :truncate, :trust, :untaint, :untrust, :untrusted?, :upto, :yield_self, :zero?, :|, :~]

As you may have guessed, methods ending with a ‘?’ return a boolean value, true or false. You can use this in your classes, but returning a boolean is not enforced by the runtime.

The first code example showed how to create accessors and mutators. The parser is flexible enough to make the call look like a simple variable assignment. This includes class-level variables. All variables in a class, regardless if they are class or object level are private by default, and can not be made public. Of course, there are reflection methods to change them and even remove them.

Making Ruby write your accessors and mutators
1
2
3
4
5
6
7
8
9
10
class Test
attr_accessor :data
attr_reader :read_only_var #creates only a 'getter' method

  def initialize
    @data='data'
    @read_only_var='immutible from outside the object?'
    @no_read_or_write='super private'
  end
end
Run the Test class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
test = Test.new

test.instance_variables
=>[:@data, :@read_only_var, :@no_read_or_write]

test.data = 42
puts test.data
=>42

puts test.read_only_var
=>immutable from outside the object?

#variables are private, but there are methods to interact with them

test.instance_variable_set '@read_only_var', :some_symbol
puts test.read_only_var
=>:some_symbol

str = test.instance_variable_get '@no_read_or_write'
puts str
=>super private

test.instance_variable_set '@no_read_or_write', 'changed!'
str = test.instance_variable_get '@no_read_or_write'
puts str
=>changed!

#Variables can be dynamically removed
test.remove_instance_variable '@no_read_or_write'
test.instance_variables
=>[:@data, :@read_only_var]

An OO language that has objects that do not know about themselves and can not manipulate themselves easily is poorly designed.

Ruby has fantastic reflection methods built-in. Granted, a lot of them are not commonly useful but when they are, they can save hundreds or more lines compared to other, more stodgy languages.

If the name of a variable or anything in the language starts with a capital letter it is a constant. String, or any other class or module name is a constant.

Judicious use of singleton objects. The references true, false, and nil are built-in singleton objects on a global scope. There is a built-in module to create singletons, when appropriate. Numeric values are singleton objects also. Singleton objects are quite useful. They are very easy to overuse and abuse. They are a useful tool when it is needed, and it is nice to have.

When the Singleton module is included (mixins are covered later) the ‘new()’ method is dynamically made private, and the ‘instance()’ method is created.

Singleton objects
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
true.class
=>TrueClass

false.class
=>FalseClass

nil.class
=>NilClass

42.object_id == 42.object_id
=>true

require 'singleton'

class SingletonTest
include Singleton
end

#can not instantiate it
st = SingletonTest.new
=>private method new called for SingletonTest:Class (NoMethodError)

st = SingletonTest.instance
st1 = SingletonTest.instance

puts st.object_id==st1.object_id
=>true #should be the same object

#Of course, calling send and then new works since new is only private not removed with the Singleton module
#This should never be done
t = SingletonTest.send :new
puts st.object_id==t.object_id
=>false


#But the built-in singletons have new removed.
NilClass.send :new
=>undefined method new for NilClass:Class (NoMethodError)

Singleton methods are used to inject new functionality into existing objects. This works on pretty much anything except built-in numeric values.

Singleton methods
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
value = '42'

puts value.respond_to? :million

def value.million
  (self.to_i * 10**6).to_s
end

puts value.respond_to? :million

puts value.million

#Output
false
true
42000000

#This can be placed in another method to act as a factory to add methods to objects
value = '42'

def million_factory obj
  def obj.million
   (self.to_i * 10**6).to_s
  end
end

million_factory value

puts value.million

#Output
42000000

Object constructors and initializers are kept separate, new() and initialize(). When creating an object new() is called: MyObject.new. The method new() is located in BasicObject. BasicObject is the base class for all classes in Ruby. initialize() is created in MyObject - it is optional if you don’t need to initialize data - and is automatically called from new().

The constructor can be overridden but is typically not a great idea unless your object really needs custom manual memory allocation. Like really really needs that. That means you will need to write a small C library to allocate and deallocate memory. If that is the case, it might be best to think twice and three times about your class. I suppose there might be a real need to do so once every 10,000 years. That can be dangerous.

For fun and maybe a good prank, you can make it completely useless so it cannot create any objects.

If you need to learn about manual memory management - and you do - learn some C. Seriously, learn C if you do not know it. C is an extremely crappy and dangerous language for 99% of use cases.

Frankly, it is a crappy and dangerous language for the other 1% where it is a good idea to use it. Learning memory management and how things work at a lower level than most languages that you might use is important, even if you never write production code in C.

Since I am meandering off-topic, learning a bit of Lisp, such as Scheme is a good idea. Smarter people than me have written extensively on the importance of learning how computers work(C language or lower level) and how computation works(Lisp).

Ruby can get somewhat close to Lisp, but I do not think that somewhat close is enough. Learn some assembler also if you can stand it. I learned two assemblers (NASM and SPIM) in college, and never enjoyed it. Not even a little, but it made me understand a bit more about what is going on. It made me appreciate languages like C and C++, even though I am more than happy to talk smack about them. As CPU design has marched on, even assembler is getting further and further from CPU instructions, but it is still useful to understand what instructions a simple loop or other branch get produced and what printing a value to a screen involves in assembler. Without knowing a bit of assembler, debugging compiled code is a nightmare.

Renaming methods is possible via the alias keyword. You could, if perverse enough, rename a method and then override the original method to do whatever or remove it. Of course, who would do such a thing? Oh yeah…

Renaming methods to “Java-fy” it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Test
attr_accessor :data, :another_var
alias set_data data=
alias get_data data
remove_method :data
end

t=Test.new
puts t.respond_to? :set_data
puts t.respond_to? :data=
puts t.respond_to? :get_data
puts t.respond_to? :data

t.set_data 42
puts t.get_data

#Output
true
true
true
false
42

The alias keyword looks a little different from attr_accessor and remove_method.

That is because alias is a keyword, and the other two are methods. There is an alias_method() method, but that is probably getting too far into the weeds for this post.

If you don’t need all of the functionality that the built-in subclasses provide there is BasicObject which is the base class for all Ruby objects, and bare-bones at that.

BasicObject
1
2
3
4
5
6
7
8
9
10
11
12
13
class Test
end

puts Test.new.methods.size

class Test1 < BasicObject
end

puts Test1.new.methods.size

#Output
58
undefined method `methods' for #<Test1:0x0000000001286b48> (NoMethodError)

The majority of the reflection methods do not exist in BasicObject, and none of the system’s functionality exists in BasicObject, but as the error message implies, method_missing() is in BasicObject. Ruby calls it a ‘blank class’. It does have a few methods defined so a class that inherits from it will have some default functionality.

I wish that the methods in Object and Kernel that are more specialized, such as those useful for interacting with the underlying system, were put into a separate object hierarchy. Something like BasicObject -> SystemObject and BasicObject -> ApplicationObject or whatever. It would shrink the number of methods available in all objects, whose classes inherit from Object, and make things a tad bit cleaner.

You can override a method, but not overload it - this may change in Ruby 3. You can still call into the parents’ version of the method if needed.

Ruby Overidding methods
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Parent
    def do_something arg
        puts 'in parent'
    end
end

class Child < Parent
    def do_something
        puts 'in child'
        super arg1
    end

    def do_something a1,a2=2,a3=4
        puts 'overloading or overriding?'
        super a1
    end
end

c=Child.new

c.do_something
=>do_something: wrong number of arguments (given 0, expected 1..3) (ArgumentError)


c.do_something 1,2,3
=>overloading or overriding?
  in parent

c.do_something 1
=>overloading or overriding?
  in parent

The reason overloading methods are not allowed is that because of default argument values in method signatures, and that would cause ambiguity. You can have the same name in the same class if one method is class level and the other is object-level since there is no ambiguity or any inheritance relationship between them. The ‘do_something’ method with no arguments in Child will never be called. It ends up getting overwritten, just like if the second do_something() was added dynamically.

You can place multiple class definitions in the same file.

The general object model is single inheritance augmented with mixins, which are covered in a bit.

The object model deserves a series of articles, and I hope to produce them soon-ish.

Continued here sigh

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.

© Vilanye. Some rights reserved.