CSC/ECE 517 Fall 2007/wiki1 5 ap

From Expertiza_Wiki
Jump to navigation Jump to search

What is duck typing?

"If it walks like a duck and quacks like a duck, it must be a duck " [3] is the philosophy of duck typing.
In terms of Ruby, duck typing refers to the tendency to be less concerned with the class of an object and more concerned with what methods can be called on it and what operations can be performed on it. Duck typing allows an object to be passed in to a method that expects a certain type even if it doesn't inherit from that type. All it has to do is support the methods and properties of the expected type in use by the method.

Consider that we have two objects: Circle and Square. 

These objects are clearly different from each other and clearly do not belong to the same class.
Assume that they have some common methods like Area() and Perimeter().

Although these methods have the same names, their implementations are completely different. Now
assume that we have a method DisplayDetails() which calls the methods Area() and Perimeter().

If we explicitly specify a type for the argument of DisplayDetails(), we will need two separate
functions to display the areas and perimeters of both the Circle and the Square. 

But by using duck typing, we take off the argument type of DisplayDetails(), thereby enabling 
us to pass objects of either type and have their methods invoked.



Let's see a small example now of how duck typing is implemented in Ruby.

def get_successor(s)
raise ArgumentError, 'No successor method!' unless s.respond_to? :succ
return "Successor of #{s} is #{s.succ}"
end
puts get_successor('a') => Successor of a is b 
puts get_successor(4) => Successor of 4 is 5 


In this example, we define a method 'get_successor' which returns the successor of the object passed to it. All the objects on which the 'succ' method is defined can be passed as an argument to the 'get_successor' method irrespective of their class. Duck typing does not rely on inheritance and polymorphism. Every single object is accepted in every single method. The only thing that matters is the capability of that object to do whatever the method is trying to do. If the object is unable to do that, the interpreter happily raises an exception.


Duck typing v/s Inheritance and Interfaces

Advantages of Duck typing over Inheritance & Interfaces:

Duck typing is one of the important features of dynamic languages in which an object's interface and attributes determine valid semantics, rather than having the language enforce rules regarding inheritance or require type casting. This allows an object to be interchangeable with any other object so long as they both implement sufficiently compatible interfaces, regardless of whether the objects have a related inheritance hierarchy.

Java uses inheritance as the mechanism for defining the type of an object. An object "is a" X if it implements X. While Ruby also supports inheritance, establishing "is a" relationships is not its main purpose. In Ruby an object is of a certain type if it behaves as that type. It must be noted, however, that while Ruby has classes, objects are not explicitly declared of to have a certain type. Conceptually, an object can be of N! types, where N is the number of methods exposed by the object.

Consider the following example, in which the salary of an employee is calculated depending on his post and the number of days he has worked in the month. We will contrast the approaches of duck typing (Ruby) and interface (Java) one at a time.

Java Code:

package duck_typing;
interface getSalary
{
   public void compute(int days);
}

class Manager implements getSalary 
{
   public void compute(int days) 
   {
      System.out.print("This manager has worked " + days + " days this month, "); 
      System.out.println("so his salary is $" + (double) days*150);
   }
}

class Clerk implements getSalary 
{
   public void compute(int days)
   {
      System.out.print("This clerk has worked " + days + " days this month, "); 
      System.out.println("so his salary is $" + (double) days*55); 
   }
}

public class Calculate 
{
   public static void main(String[] args)
   {
      getSalary[] emp = {new Manager(), new Clerk()}; 
      for (int i = 0; i < emp.length; i++)
         emp[i].compute(20); 
   }
}


Output:

This manager has worked 20 days this month, so his salary is $3000
This clerk has worked 20 days this month, so his salary is $1100

In the above code, we have created an interface 'getSalary' which has the abstract method 'compute'. The two classes Manager and Clerk need to implement this interface and define the method 'compute', to calculate the salaries of its objects. As can be seen from the code, we can compute the salaries of both the manager as well as the clerk, even though they belong to different classes.


Now let's see how the same can be implemented in Ruby using duck typing.

Ruby code:


class Manager
def compute(days)
print "This manager has worked #{days} days this month, " 
puts "so his salary is $#{days * 150}"
end
end

class Clerk
def compute(days)
print "This clerk has worked #{days} days this month, "
puts "so his salary is $#{days * 55}" 
end
end

emp = [Manager.new(), Clerk.new()]
emp.each {|em| puts em.compute(20) }


Output:

This manager has worked 20 days this month, so his salary is $3000
This clerk has worked 20 days this month, so his salary is $1100


It can clearly be seen that implementing the above code is much simpler and less verbose as compared to java. In Ruby, we just created two classes, Manager and Clerk which implemented the method 'compute'. We were able to invoke the 'compute' method with the objects of different classes.
Thus it can be said that duck typing is a big time saver when we write codes, as the verification that the object does respond to different methods is not made when the object is passed as an argument to the method, but is made on the invocation of the said methods. And this is the reason why arguments to methods are usually not typed all.


We can sum up the advantages of duck typing as follows:

• Granularity [1]: Duck-typed functions are the least dependent on the types of the arguments passed.On 
  the contrary, interfaces may carry extra requirements, like some methods that are not required for
  the function in question. Although we can split the interfaces by keeping only a certain number of
  required functions in them, the resulting increase in the number of interfaces can cause confusion.

• Adaptability [1]: Thanks to the independence of duck-typed functions from the type of arguments passed, 
  a duck-typed function can be adapted to contexts that it was not built for initially, with a minimal 
  effort on the part of the caller.

• Convenience [1]: Since the types of arguments of methods are not typed, it eliminates the need to 
  write separate caller functions based on the type of arguments to be passed.


Disadvantages of Duck typing over interfaces:

• The first drawback of duck-typing is the lack of compiler safety net. If enough care is not taken
  while writing a piece of code, there is little guarantee that it will run successfully. The
  problem isn't with duck typing, but in testing the 'duckness' of an object. Duck typing takes
  away a lot of error checking performed by the compiler and replaces it with unit testing to be
  performed by the programmer. However, in most cases programmers prefer the compiler to find and
  help avoid as many errors as possible before running their own unit test cases, which can
  themselves be buggy and incomplete.  

• The second drawback is the lack of a precise contract between the calling & the called methods.
  The only way to find it out is to analyze the body of the called method, which can be impractical
  at times, especially when one has to deal with a third party API. The most common approach to
  mitigate the issues caused by this is to provide documentation. But even that is not really
  advisable, as the documentation can get too verbose and less direct (informative). Also, there
  is a serious likelihood that the documentation may not be in sync with the code, as the code
  evolves with time.

• The third drawback is the difficulty in maintaining duck typed code. Although duck typing is
  beneficial while writing small applications as it facilitates fast coding, speed is of little
  significance in large and complex applications if one ends up writing fragile code which is hard
  to maintain. Since a major bulk of the cost of such codes over their lifetime is spent on their
  maintenance, anything that compromises the maintainability of code is highly suspect. Static
  typing is more beneficial in such cases, as it catches many errors that may not be apparent at
  first, but might surface over the lifetime of the application.  

See also

[1] http://cdsmith.twu.net/types.html
[2] http://coderoshi.blogspot.com/2007/09/5-reasons-static-typing-sucks.html
[3] http://www.oopcenter.com/article/ruby-on-rails/ruby-classes-and-objects.html

References

[1] http://www.alittlemadness.com/?p=28
[2] http://www.akropolix.net/rik0/blogs/category/programming/ruby/
[3] http://boo.codehaus.org/Duck+Typing
[4] http://def-end.blogspot.com/2006/11/lame-duck-typing.html
[5] http://blog.newatlanta.com/index.cfm?mode=entry&entry=116C5A99-A4A6-12B9-3D6013713A815D9C