CSC/ECE 517 Fall 2012/ch2b 1w59 nm
SaaS - 3.7. Mixins and Duck Typing
This wiki is aimed at acting as a complementary read to the SaaS - 3.7. Mixins and Duck Typing lecture. This lecture describes three of the most important fundamentals of the Ruby language: Duck-Typing, Modules and Mixins.
Introduction
- Duck-typing is one of the main features of many object-oriented languages. Duck-Typing describes how a language like Ruby, treats its objects as compared to a language like JAVA. We describe the beautiful features of Duck-typing and also its negative side-effects.
- Modules define a namespace. It provides a mechanism to club several things together which naturally do-not form a common class. With modules, programmer does not have to worry of possible name-clashes of method name's with other methods written by different programmers. Modules handles this by making use of namespace.
- Mixins extend from modules. modules are not classes and hence we cannot instantiate them. However, when modules are 'include'ed in a class, all its methods are available to that class. We say modules are 'mixed in' to the class or in other words 'mixins'. Modules which are mixed in behave as superclass and provide multiple inheritance.
Duck Typing[1]
What is Duck Typing
“If a object walks like a duck, quacks like a duck then treat it as a duck”
[2]
This is the ‘mantra’ of Ruby language and the most pleasing aspect of the language. What it means is that, whether an object responds to a method?, is only important and not the type of the object.
If a dragon responds to methods like swim and quacking, ruby is happy to treat dragon as a duck without having a concern of whether it actually is a duck or not. This gives powerful tools for a ruby programmer.
This is possible in Ruby because we don’t declare the 'Type[3] of variables or methods like in JAVA; in Ruby, everything is an object and each object can be individually modified, irrespective of its implementation in the class to which it belongs.
Consider the following example taken from the SAAS lecture:
my_list.sort [5, 4, 3].sort ["dog", "cat", "rat"].sort [:a, :b, :c].sort
Here, the same function, sort, is responding to an array of integers, strings and literals. This is classic Ruby duck-typing at work. How sort works is that it compares the elements which are needed to be sorted. So as long as the elements can be compared, sort function works. In the above example, the array of integers, strings and literals implement the comparison operator and hence the same sort function works for all of them.
Why in Ruby and not in JAVA?
The reason for duck-typing to be easily possible and implemented in language like Ruby and not in language like JAVA is that these 2 languages treat ‘type’ differently. In JAVA or C++, type means class. When someone asks the type of “abc”, we say it is String. So, such language allows String objects to refer to only other String objects. String a; a will never be allowed to refer to anything other than a string, like a=5.
While in Ruby, type refers to how the object reacts to a method or in other words does the object have a particular capability. Type refers to this capability. While a class to which the object belongs provides the initial capabilities, from that point on, the object is free to extend its capabilities independently of its pre-defined class. So,
a= ”abc” a= 5 are two perfectly acceptable statements in Ruby.
This is duck-typing. A classic example[3] of how a scripting language, JavaScript, not implementing duck-typing can lead to some bad code.
Duck-Typing in action
class American def football puts "We play it with hands" end end class European def football puts "We play it with legs" end end class Martian end footballManiac = [American.new,European.new] footballManiac.each {|game| game.football}
Output of this program is We play it with hands(next line) We play it with legs
This is because of duck-typing. footballManiac first acted as an American class object and since that class responded to the football method, it executed it. Then it acted as an European class object and again that class responded to the method, it executed it. In static-typed languages, we would have to type cast each object and even then we might get compile time or run time error. Nothing of that sorts in Ruby. Now let us also create a Martian class object.
footballManiac = [American.new,European.new,Martian.new] footballManiac.each {|game| game.football}
On executing it, we get the following error
undefined method `football' for #<Martian:0x3425358> (NoMethodError)
This is obvious because martians do not play football. But the point is, Ruby still checked the martian class and when it found that the object does not respond to the method, it complained.
The other side to Duck-Typing
As with everything in life, there is always 2 perspectives about its usefulness. Following are some of the criticism against duck-typing :
Lack of compiler security
There is no help from compiler for a programmer error like assigning a string to an integer and so on. Only when you run the code, the output might be funny and that’s when you realize a probable mistake in assignment. In a static-type language, there is always a compiler check and hence the problem does not arise. Obviously this problem can be alleviated by proper and exhaustive testing. But as we all know, no one can guarantee 100% code tests.
Contract between caller and the callee
In a static type language, the caller knows the exact ‘type’ of parameters required by the callee and also the return-type from the callee. In dynamic type languages, no type is provided. So to understand what the callee expects, one has to look at the code and understand. This is difficult in most of the cases and mainly for third-party code. Again this problem can be solved by providing proper documentation of each API and the pre-requirements for execution. But who can guarantee that the documents correctly map the requirements?