Forget the strings, integers, doubles, floats and even the semantic ones which help simulate the real world types, err, including ducks! Considering that I had come from the disciplined world of static typing and dynamic typing this idea of duck typing was quite a shock to me when I got introduced to Ruby. Avoiding the reactive complete dismissal of the idea I ventured further and here is what I got out of it.
Static, Deferred and Dynamic Typing
Static typing enforces specification and identification of all the types involved. If I am developing a duck pond that accepts only ducks, then the pond will accept objects only of the type duck. So, a bird has to convince the compiler that it is a duck. Programmatically, the variable for the duck has to be of the type duck. However, it will be difficult to handle a pond to include ducks as well as swans. Static typing creates restrictions and hurdles for programmers in such cases.
So designers started using abstraction. For example, bird-that-can-swim is the abstract type, and duck and swan are the concrete types. A duck and swan does everything that a bird does and possibly more. However, the parts of code that deals with birds becomes complex, as the concrete type of the bird still had to be identified at the compile time. The solution is to defer identification of the concrete types to the run time phase. This is dynamic binding. Code can still deal with just the abstract types and dynamic binding is used to identify the concrete types at the run time. The duck will now have to convince the compiler that I am a bird-that-can-swim, I will tell you later whether I am a duck or swan or something else. Programmatically, the variable for the duck can be a bird, and the one for the swan can also be a bird. This enabled writing extensible, manageable, readable code as it did not need to drill down to identify the exact type.
Dynamic Typing extended this to the fullest, the languages that supported it are called dynamic languages. All types are identified at run time. Variables do not have types, their values have. The type of the variable is determined by the type of its value. Essentially, the variable can point to different values of different types at different types and inherit that type. The programmers were not bothered to specify and then comply to strict typing. The duck will not have to convince the compiler at all, it will be identified directly during the execution.
After already experiencing and getting used to the strongly typed languages, I personally never felt that dynamic typing had a lot of advantages. In fact, I always thought that, rather than a feature, dynamic typing was convenient for interpreted scripting languages. Unlike static typing which requires compiling, the interpreters could directly execute the code and opt out of identification of types. There are even optimization techniques for some dynamic languages where the programmer can specify some types so that they are identified at compile time. Such languages supported a phase of compilation and by default execute the code.
As we kept on deferring the identification of types, the performance was affected. This was because instead of directly executing the code the run time phase had to put additional effort to identify the types.
The in thing today is duck typing. Duck typing ducks typing. Duck typing is considered as a special form of dynamic typing, but I think it involves a paradigm shift. The types are not identified by the variables or the values, they are identified by the capabilities. No types need to be identified for execution. The duck can now completely duck its identification and still get used. For example, the pond that we are writing will not identify the duck at all.
The philosophy being that if I can walk like a duck or swim like a duck then I am a duck.
This might seem to be a good thing, but I don’t it is true always. Losing types is like lossing identification. The real world is not always about capabilities, it is also about identification. While the duck can swim, so can I and so can the fishes, it wi be difficult to implement that the ponds should accept only birds that can swim. It is not required only that I can swim, but also who I am. This kind of constraint has to be done only by trusting the developer, who knows what he/she is doing.
The other disadvantage is that now the code does not specify what aspect of an object is being used. For example, our pond might use only this attribute that ducks can swim. In the static typing or dynamic binding cases, the type of parameters could specify the aspect of object being used, something like
pond (ISwimmable), where ISwimmable could be an interface that represents capability of swimming – in short, the code talks. Now I will have to read documentation of the code or the code itself to find out what aspect is being used. This problem can escalate if I am using a lot of third party libraries which do not tell me what types they expect.
The common aspect of all of these is that there still are objects and all of them have interfaces. In the static typing, dynamic binding and dynamic typing, this interface is explicitly mentioned in the code, whereas it is implicitly used in duck typing.
That being said, the above mentioned problems are in a scenario where there are constraints, e.g., ponds should accept only birds-that-can-swim. However, in the case of a pond for everyone, duck typing would be immensely useful. I find this kind of nature in Web programming, where there is discoverability and interaction with others based on capabilities rather than identification. It might be an ideal choice for REST and ROA
One other factor that can affect the applicability is that the developer has to take over the responsibility of using the right objects (semantic types) from the compiler. In earlier cases, the compiler could warn that the code expects a bird-that-can-swim and you have passed a lion or a human. Similarly, if a product goes under the hands of many programmers, the talking code can help them understand the code in an easier way, after all programming is understanding. My experience in the industry – all the developers in the team cannot be relied on for this. The bigger the team bigger this problem will get. However, it will be extremely productive with a short and tightly-bound team.
Regarding code quality, I think it is a non-factor. Some of us are innovative enough to write rubbish code whatever tools and languages given.
There are many controversies and wars as to what is better among the corresponding technology loyalists. I would rather take a different stance that no technology is applicable everywhere. I should be loyal to the customer and the user rather than to a technology. It definitely required continuous research but it can help you select the right programming language.