POSTS / Duck Typing: An Interesting Nominal Type System
Yesterday, I was asking Github Copilot questions about TypeScript’s Type System and it threw me a concept — ‘Duck Typing’.
What is Duck Typing?
From Wikipedia1: In computer programming, duck typing is an application of the duck test—”If it walks like a duck and it quacks like a duck, then it must be a duck“—to determine whether an object can be used for a particular purpose. With nominative typing, an object is of a given type if it is declared as such (or if a type’s association with the object is inferred through mechanisms such as object inheritance). With duck typing, an object is of a given type if it has all methods and properties required by that type.2 Duck typing may be viewed as a usage-based structural equivalence between a given object and the requirements of a type.
IMO, I think it’s more like something other than a type system. In Duck Typing, the definition of TYPE is less important than the usage of OBJECT, which means, the type of one object depends on what it can do or what you want it to do. Ducks can be animals or food.
Examples
Pseudocode:
function calculate(a, b, c) => return (a+b)*c
example1 = calculate (1, 2, 3)
example2 = calculate ([1, 2, 3], [4, 5, 6], 2)
example3 = calculate ('apples ', 'and oranges, ', 3)
print to_string example1
print to_string example2
print to_string example3
Expected Output:
9
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
apples and oranges, apples and oranges, apples and oranges,
What can Duck Typing do?
From Python Docs2: A programming style which does not look at an object’s type to determine if it has the right interface; instead, the method or attribute is simply called or used (“If it looks like a duck and quacks like a duck, it must be a duck.”) By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using type() or isinstance(). (Note, however, that duck-typing can be complemented with abstract base classes.) Instead, it typically employs hasattr() tests or EAFP programming.
With Duck Typing, one programming language can be very flexible in its coding style. Duck Typing is more likely a patch to the existing type system. Programmers can avoid the type test and call the methods directly and just handle possible exceptions. As Python Doc says, it’s a style which makes it easier to ask for forgiveness than permission3.
One usage of Duck Typing is the data sending issues between frontend and backend. Take me for example, I define a type called ’SearchResult’ and give it some fields then fetch from the backend. Few days later I’d like to update my backend to return more fields in one ’SearchResult’. I can directly deploy the newer backend and there’s no need to worry about whether the frontend will crash as the strict type system of TypeScript.
Also, Duck Typing allows polymorphism without inheritance. For example, in cpp if one guy wants to implement a function bar for A and B which calls methods foo, there are some ways:
- write a template and depends on compiler to generate codes for both types, or
- use function overloading and write two functions himself, or
- make A and B inherit from C and use C as the type of parameter.
Here are the codes:
// Method 1: Function Template
#include <iostream>
class A {
public:
int foo() { return 1; }
};
class B {
public:
int foo() { return 2; }
};
template <typename T> int bar(T t) { return t.foo() * 2; }
int main() {
A a;
B b;
std::cout << bar(a) << std::endl;
std::cout << bar(b) << std::endl;
return 0;
}
// Method 2: Function Overloading
// ...
int bar(A t) { return t.foo() * 2; }
int bar(B t) { return t.foo() * 2; }
// ...
// Method 3: Class Inheritance
// ...
class C {
public:
virtual int foo() = 0;
};
class A : public C {
public:
int foo() { return 1; }
};
class B : public C {
public:
int foo() { return 2; }
};
int bar(C &t) { return t.foo() * 2; }
// ...
As we can see, though not much work to do, languages with Duck Typing can finish that function with a easier way like function bar(t) { return t.foo() * 2 }
.
4
Summary: Pros. and Cons.Advantages of Duck Typing:
- Flexibility: Duck typing provides a flexible way of programming, allowing developers to write code that is more resilient to change.
- Simplicity: Duck typing makes the code easier to write and read, as there is no need to define types explicitly.
- Better abstraction: With duck typing, developers can create more abstract code that is not tied to specific types, allowing for easier code reuse.
- Easier maintenance: Since duck typing allows for greater flexibility and better abstraction, code that is written using this approach can be easier to maintain.
Disadvantages of Duck Typing:
- Type errors: Duck typing can lead to type errors at runtime, as there is no compile-time checking of types. This can be a disadvantage for large or complex projects, where errors can be harder to detect.
- Difficulty in understanding code: Since the type of an object is not explicitly defined, it can be harder to understand the code and its behavior.
- Debugging can be harder: Debugging can be harder with duck typing, as errors can be harder to detect and trace.
- Performance: Duck typing can have a negative impact on performance, as the runtime has to do more work to determine the type of an object.