Page 1 of 1

Dynamic Typing A taste and overview of the dynamic type system

#1 dorknexus  Icon User is offline

  • or something bad...real bad.
  • member icon

Reputation: 1255
  • View blog
  • Posts: 4,618
  • Joined: 02-May 04

Posted 30 June 2010 - 08:28 PM

Ruby offers some very powerful features, one of which is dynamic typing which goes hand-in-hand with meta programming. We will briefly introduce both here.

What is Dynamic Typing?
Dynamic typing is the ability of a program to infer, analyze, and manipulate types at run time, dynamically. In other words, dynamic typing allows us to change the way a class (and thus all instantiated objects of that class) operates at run time. This is really powerful because it allows us to have our program adapt, at run time, on the fly, without having to reprogram and recompile the program.

Part of the dynamic type system that lends Ruby its power is a feature called "open classes." This means that classes are never closed to manipulation. We can inject new functionality into a class at run time if we please, and we can keep manipulating the class definition to our hearts content. People coming from explicitly and statically typed languages might be confused at this point. If you change a class definition at run time, how do all the other components in the program know how to interact with the modified interface/type. The answer is: duck typing. In Ruby, there are no explicit types, yet the interpreters will still perform type checking.

In fact, Ruby is a strongly typed language. This may come as a surprise to folks who are used to explicitly and/or statically typed languages. Ruby will not allow incompatible types to interact. But how? In a duck typing system, the interpreter will analyze the object that you are trying to operate on at the moment you attempt to operate on it. If the object does not feature the method you are trying to invoke, then it will throw an error. If it does feature the method you are trying to invoke, then it will allow it and everything continues as normal.

To show the power and simplicity of dynamic typing, we are going to implement our own attr_reader, attr_writer, and attr_accessor methods. For those who don't know, those are all Ruby methods which setup getters and setters inside of a class. They're use looks like this:

class MyClass
    attr_accessor :var1, :var2, :var3
end



This will add setter and getter methods for all three variables defined. Awesome! But how does this work? Is attr_accessor some fancy keyword in Ruby that gets handled by the interpreter specially? Nope, it is a method, written in Ruby. That means we can mimic its functionality and add functionality at run time.

Here we go. We will add in our methods to the existing class "Class." This will make our methods available to all classes as Class is a super type to all other classes. This allows us to treat classes in ruby as first class objects.

class Class
    def attr_reader2(*vars)
        vars.each do |symbol|
            define_method(symbol) { instance_variable_get "@#{symbol}" }
        end
    end

    def attr_writer2(*vars)
        vars.each do |symbol|
            define_method("#{symbol}=") { |value| instance_variable_set("@#{symbol}", value) }
        end
    end

    def attr_accessor2(*vars)
        attr_reader2(*vars)
        attr_writer2(*vars)
    end
end



So what did we do here? Let's take a look:
Three functions were added to the existing "Class" class: attr_writer2, attr_reader2, attr_accessor2. They were all suffixed with the number 2 as not to overwrite the existing methods with the same names (remember we were just mimic'ing the existing functionality).

This is dynamic typing in action. The "Class" class has already been defined in the standard library when we load up this script. Because it has already been defined, what looks like a class definition is actually just accessing an open class. That is, we are adding to the class rather than overwriting it an redefining it. So the "Class" class maintains all of its existing functionality defined by the standard library, but this code tacks on three additional methods.

So, to re-iterate, we have modified the class definition at run time. Cool!

But what do these methods do? Well, they themselves do more dynamic typing. In this case they are doing some meta programming. If you aren't familiar with the term meta programming, Wikipedia has a fantastic article about it here. In summary though, meta programming is about having programs write and/or manipulate themselves or other programs.

These three methods we wrote are going to, in turn, define new methods based on symbols passed to them. Specifically, they are defining getter and setter methods for each symbol passed in. They add new methods at runtime using another meta programming method called "define_method" which does just that; defines a new method in the containing class. Let's take a look at an example.

class MyClass
    attr_accessor2 :var1, :var2

    def initialize(var1, var2)
        @var1, @var2 = var1, var2
    end
end



Alright, so that doesn't seem like much code, but that's where the beauty lies. The attr_accessor2 method will add 4 new methods to the "MyClass" class. So, at run time, the class is being expanded to look like this:

class MyClass
    def initialize(var1, var2) 
        @var1, @var2 = var1, var2
    end

    def var1
        @var1
    end

    def var1=(value) 
        @var1 = value
    end

    def var2
        @var2
    end

    def var2=(value)
        @var2 = value
    end
end



So those meta programming methods did a lot of work for us and shifted the workload of redundant code onto the machine rather than the user (where it belongs). Awesome!

This post has been edited by Dark_Nexus: 30 June 2010 - 10:30 PM


Is This A Good Question/Topic? 2
  • +

Replies To: Dynamic Typing

#2 cfoley  Icon User is offline

  • Cabbage
  • member icon

Reputation: 1910
  • View blog
  • Posts: 3,954
  • Joined: 11-December 07

Posted 01 July 2010 - 02:18 AM

Oh really nice. I'm just getting into Ruby and thought attr_accessor was just syntactical sugar. Thanks for the explanation!
Was This Post Helpful? 0
  • +
  • -

#3 MitkOK  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 32
  • View blog
  • Posts: 403
  • Joined: 09-August 07

Posted 18 July 2010 - 02:37 PM

It's a really, really bad idea to monkey patch the core classes or the standard library, but for the purpose it's ok.
I would implement it as a mixin module:

module MyAccessors

  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def attr_reader2(*arg)
      arg.each do |symbol|
        define_method(symbol) { instance_variable_get "@#{symbol}" }
      end
    end

    // the rest of the class methods

  end

end


class MyClass
  include MyAccessors
  
  attr_reader2 :name

end


We have hook method - included, which will extend the class we include in with the class method

This post has been edited by MitkOK: 18 July 2010 - 02:42 PM

Was This Post Helpful? 1
  • +
  • -

#4 dorknexus  Icon User is offline

  • or something bad...real bad.
  • member icon

Reputation: 1255
  • View blog
  • Posts: 4,618
  • Joined: 02-May 04

Posted 01 February 2011 - 11:29 AM

I do prefer the module method to monkey patching core classes and I think it's excellent advice.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1