Friday, April 29, 2011

Ruby Dynamics

When we talk about Dynamic Languages, it means different things to different people. Is a language dynamic because of dynamic typing?. is it because it can execute scripts of arbitrary code dynamically?. is it because of duck typing?. is it because we can extend it on runtime?. Or is it because all of the previous reasons together?.

I think that each of the features (and more) add to the level of dynamism of the language.

In this article I’ll explore some of the characteristics that make Ruby an incredibly dynamic language. I’ll name some of these features first and then go briefly into what they are and how they work.



Dynamic Typing:

This characteristic is easy, just meaning that there doesn’t exist a type for variables at compile time, just at runtime, and it can be changed during the variable lifecycle as this irb session shows:

irb(main):001:0> a="dsd"
=> "dsd"
irb(main):002:0> a.class
=> String
irb(main):003:0> a=5
=> 5
irb(main):004:0> a.class
=> Fixnum
irb(main):008:0> local_variables
=> [:a, :_]



Duck Typing

This is another common characteristic of dynamic languages, it’s meaning is that regardless the type of a variable if it responds to a particular message, then it can receive that message and act accordingly:

For example, the message “fly” can be sended to a Airplane, or a Bird. In a language like Java, you might need to define an Interface like “FlyCapable” or similar that defines the fly method, in Ruby (and other languages with Duck Typing) you can just do:

  1. class Bird
  2.  def fly
  3.     puts "I am a flying bird"
  4.  end
  5. end
  6.  
  7. class Airplane
  8.  def fly
  9.     puts "I need a motor, but also fly"
  10.  end
  11. end
  12.  
  13. def make_entity_fly(entity)
  14.  entity.fly
  15. end
  16.  
  17. bird = Bird.new
  18. plane = Airplane.new
  19.  
  20. make_entity_fly bird
  21. make_entity_fly plane


and when you run it, you get

I am a flying bird
I need a motor, but also fly




Extending existent classes at runtime.

In this point i am not refering to extend in the sense of inheritance but instead actually extending the functionality of a class that already exists, for example adding a new method to the String class. There are a few ways to do this, you can reopen the class, or you can use the class_eval method to do it.

let’s see both, let’s suppose we want to add a method to the String class we can just do:

  1. class String
  2.  def my_method
  3.     self + " and some extra "
  4.  end
  5. end
  6.  
  7. string = "Original String"
  8. puts string.my_method
  9. #And this is still a normal String with the common String methods of course
  10. puts string.capitalize



Using class_eval:

  1. String.class_eval do
  2.  define_method :my_method do
  3.     self + " and some extra "
  4.  end
  5. end
  6.  
  7. string = "Original String"
  8. puts string.my_method
  9. #And this is still a normal String with the common String methods of course
  10. puts string.capitalize



Modules

Modules is one of the nicest things in Ruby, easy to understand and a very powerful feature. Modules allow you to isolate behaviour in one place and use this behaviour (methods) from classes that include the module, as if the methods where defined in the class itself, allowing its instances to use them. An example to illustrate this better.

  1. module StartAndStop
  2.  def start
  3.     @state = "started"
  4.  end
  5.  def stop
  6.     @state = "stoped"
  7.  end
  8. end
  9.  
  10. class Car
  11.  include StartAndStop
  12.  def state
  13.     puts @state
  14.  end
  15. end
  16.  
  17. car = Car.new
  18. car.start
  19. car.state
  20. car.stop
  21. car.state



And the output

ruby Ruby-1.rb
started
stoped




Ruby is Object oriented no class oriented

I didn’t realize this until i started working with Ruby, But Java is really more Class oriented than object oriented. In Java one object’s features are forever bound to it’s class (and superclass) definitions. In Ruby by contrast, Objects can have a life of their own independent from their class, So in Ruby classes work as a template for creating objects, but from that moment the object lives on its own:

  1. class Person
  2.  def talk
  3.     puts "hola"
  4.  end
  5. end
  6.  
  7. p1 = Person.new
  8. p2 = Person.new
  9.  
  10. def p1.scream
  11.  puts "HOLA!!"
  12. end
  13.  
  14. p2.instance_eval do
  15.  undef :talk
  16. end
  17.  
  18. p1.talk
  19. p1.scream
  20.  
  21. p2.talk



And the output

ruby Ruby-1.rb
hola
HOLA!!
Ruby-1.rb:20:in `
': undefined method `talk' for # (NoMethodError)





Extending the singleton class of an object.


In Ruby every object, apart from the classes it extends explicitly, has associated a class for it’s own. This classes are known as Eigenclasses or Anonymous classes. We already see a little of how to extend this classes with behaviour when we saw the definition of the scream method directly in the object. However if you want to open a Eigenclass of an object and keep it open to add something to them you can do it like:

  1. a = String.new
  2.  
  3. class << a
  4.  def append_salute
  5.     self << "Hola"
  6.  end
  7. end
  8.  
  9. a.append_salute
  10. puts a




Extending using modules dynamically


We already saw that you can add behaviour to a class with the inclusion of modules. And you can also include modules directly in an object (actually to it’s Eigenclass) You can do this two ways:

1)
  1. module DummyModule
  2.  def method_dummy
  3.     puts "inside dummy method"
  4.  end
  5. end
  6.  
  7. a=String.new
  8. a.extend DummyModule
  9. a.method_dummy



2)
  1. module DummyModule
  2.  def method_dummy
  3.     puts "inside dummy method"
  4.  end
  5. end
  6.  
  7. a=String.new
  8.  
  9. class << a
  10.  include DummyModule
  11. end
  12. a.method_dummy





Using eval and similars

We already saw about class_eval and instance_eval before, the other method to know about is the eval method. It basically allow you to execute arbitrary Strings of code as Ruby code:
  1. string_of_code = "class ClaseDummy
  2. def method_dummy
  3.    puts 'dummy'
  4. end
  5. end"
  6.  
  7. eval(string_of_code)
  8. a=ClaseDummy.new
  9. a.method_dummy


Creating a new type in runtime.

In Ruby, a class is an object of type Class, normally identified by a name as a Constant. So it is easy to create a new type that doesn’t exist:

  1. Person = Class.new do
  2.  def talk
  3.     puts "hola"
  4.  end
  5. end
  6.  
  7.  
  8. p=Person.new
  9. p.talk


And the output

ruby Ruby-1.rb
hola





Dynamic Dispatch


Dynamic dispatch means allowing to send messages (call methods) to objects, determined at runtime. You don’t know the method you want to call until runtime:

  1. class Person
  2.  def talk
  3.     puts "hola :)"
  4.  end
  5. end
  6.  
  7.  
  8. p=Person.new
  9. metodo = "talk"
  10.  
  11. p.send metodo


And the output:

ruby Ruby-1.rb
hola :)


Defining methods dynamically

This means creating methods in Runtime that don’t yet exist at compile time. There are a couple of ways to do this. One is:
  1. class Person
  2.  def talk
  3.     puts "hola :)"
  4.  end
  5. end
  6.  
  7.  
  8. new_method="scream"
  9.  
  10. Person.class_eval do
  11.  define_method new_method do
  12.     puts "HOLA!"
  13.  end
  14. end
  15.  
  16. p=Person.new
  17. p.scream
  18. puts Person.instance_methods false



And the output

ruby Ruby-1.rb
HOLA!
talk
scream



Ghost Methods


Ghost methods are methods that not really exist but you can still call. How cool is that?. They leverage a special method in every Ruby object named missing_method that gets called when calling a method on an object that doesn’t exist in all its lookup path.

  1. class Person
  2.  def method_missing (name,*args,&block)
  3.     if name == :talk
  4.      puts "hola "+args[0]    
  5.     else
  6.      super
  7.   end
  8.  end
  9. end
  10.  
  11. p=Person.new
  12. p.talk "carlo"
  13. p.scream



And the output

ruby Ruby-1.rb
hola carlo
Ruby-1.rb:6:in `method_missing': undefined method `scream' for # (NoMethodError)
from Ruby-1.rb:13:in `
'

This was just a small introduction, all these techniques and many more can be combined and extended to achieve really powerfull results.

A couple of good books on Ruby and the subjects of dynamics and metaprogramming are:



2 comments:

Anonymous said...

Nice teaser, thanks! You could elaborate class_eval and its friends more, maybe in a separate post...

Some of my articles in this topic:
Ruby's class system
Ruby blocks and closures

Carlo on Data/Software engineering said...

Thanks for your comment, I'll take a look at your articles.

I'll try to elaborate in new articles some of the points, I was thinking particularly in Ghost Methods, but also the Eval family.