DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report

The Duck is a Lie

Giorgio Sironi user avatar by
Giorgio Sironi
·
Jun. 25, 12 · Interview
Like (0)
Save
Tweet
Share
10.70K Views

Join the DZone community and get the full member experience.

Join For Free

What follows is my experience with Java, PHP and Ruby. I mainly use PHP as a dynamic language that supports duck typing but also the definition of Java-like interfaces, but does not force any of the two approaches as you can define interfaces whose method arguments accept any variable or not using interfaces at all.

Is duck typing that a revolution?

Misconceptions I've seen

First of all, duck typing is not a means for making objects work together even when designed by different people or projects. For example, given a send() method it could be implemented in so many versions (like send(), sendX(), send(x, y)) that it is highly unlikely that without defining a common interface the objects will call each other correctly. Even when the signature seems similar, the objects passed as the first or second or n-th argument could be actually different.

Moreover, it is possible to encounter different implementations of send() which actually weren't meant to conform to the same interface: think of sending an e-mail and sending a Facebook notification. This clash of interfaces would be detected by a type system or at runtime in hybrid languages, but results in strange errors (like undefined methods error when a message is sent to these arguments.)

Duck typing works well when the implicit interfaces are well-known: for example, when overriding the + operator, or the equals() and compareTo() methods.

Explicit interfaces

That's not to say that static checks and explicit interfaces are enough to prevent all errors, as you can always implement an interface in an inconsistent way and (without design-by-contract) there is nothing that will save you:

interface SortingAlgorithm
{
    public function sort(array $objects);
}
class BubbleSort implements SortingAlgorithm { ... }
class Quicksort implements SortingAlgorithm { ... }
class MergeSort implements SortingAlgorithm { ... }

What's the difference between these two classes?

  • Quicksort in efficient implementations is not a stable sort (an algorithm that leaves objects with equal keys in the same order as in the original collection). Mergesort instead is always a stable sort.
  • Are the algorithms sorting in ascending or descending order?
  • And on which key?

Actually, we could argue that these are the kind of information hiding that we want, so that we are able to change our sorting mechanism after a user request or a new functional requirement just by swapping in an object. But Java and PHP interfaces are not contracts: the only specify typing requirements but no preconditions or postconditions.

With duck typing, the picture would be (keeping PHP as the language for consistency):

class BubbleSort {
    public function sort(array $object) { ... }
}
class QuickSort {
    public function sort(array $object) { ... }
}
class MergeSort {
    public function sort(array $object) { ... }
}

But in this case we don't know anymore that there was the intent of the original developer was to make these algorithms/objects compatible, or what requirements we should follow to implement a new one. Some of these problems can be alleviated with the acts-as pattern, but I believe not all of them.

The acts-as pattern

In Ruby, one of the most popular duck-typed languages, it is common to implement duck typing with an external module (to be mixed in in implementing classes) that accepts calls to a primitive named acts_as_something.

For example, this definition would add several methods to the class like first?, insert_at and so on:

class Product < ActiveRecord::Base
    acts_as_list
end

You can actually pass methods and procs (anonymous functions) to an acts-as primitive, effectively providing an implementation where the external interface is always consistent (because it consists in methods defined inside the module.)

However, in these cases pattern is the realization of a Template Method mechanism (or a Bridge pattern at best) where the internal interface between the object and the module suffers the same fate as the original sort() function:

  • what I should pass to acts_as_something?
  • When one of the arguments is a proc, how many arguments it accepts and which messages they accept in turn?

Moreover, if you had to implement every implicit interface with Template Methods you'll quickly grow your classes. Ruby "fixes" the problem by allowing multiple inheritance (that's what modules really are), so that you can extend as many classes as you need to provide as many Template Methods as you need.

The single callback fallacy

What I am starting to hate are PHP methods that think that all closures are equal. Their duck-typed signature typically is:

public function doSomething(callable $callback) { ... }

where $callback is a closure or an object defining __invoke(); something that you can call with $callback(/* arguments in here */).

The problem? I have to ask many questions when I see this signature:

  • how many arguments does $callback take? Does it have any?
  • Which methods each of this argument exposes? That is, I can call $firstArgument->x() or $secondArgument->y()?
  • If I'm consciously violating the Law/Suggestion of Demeter, and calling methods on the result of $firstArgument->x(), what can I call?
  • What should $callback return, if anything?

That's why I now prefer in more and more cases to define explicit interfaces even where I have a single method:

interface ThatCallback
{
    public function __invoke(AClass $firstArgument, BClass $secondArgument);
}

The problem with this approach in PHP is that you can't define a private inner class implementing this, so you'll need a new source file:

class MyCallback implements ThatCallback
{
    public function __invoke(AClass $firstArgument, BClass $secondArgument)
    {
        ...
    }
}

Java does it better by allowing you to even create anonymous classes inside your methods:

new ThatCallback() {
    public void invoke(AClass firstArgument, BClass secondArgument) {
        ...
    }
}

Duck typing is the absence of types: to provide documentation for an interface means writing down which messages it accepts, and in turn what the arguments and the return value of the implementing methods accept. With an explicit interface, you just refer to the type AClass and BClass from any place in your code, breaking the recursive specification of messages at the first level. The only alternative is not to document.

Conclusions

Explicit interfaces are centers that collect in a single place all the information you want to provide about a contract, avoiding the duplication of the list of accepted messages of an object in all the locations where it is cited as a collaborator, an argument to a method or a return value.

I don't want to be force to define an interface, but to have the ability to do so in the time of need. Static checks like interfaces only catch a certain category of errors, but their utility is not limited to constraining the programmer but also to the reification of a concept.

Interface (computing) Duck typing

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Top 5 Data Streaming Trends for 2023
  • 11 Observability Tools You Should Know
  • Testing Level Dynamics: Achieving Confidence From Testing
  • Choosing the Right Framework for Your Project

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: