Question

Statically-typed languages such as Java afford the benefit of compile-time checking of types - you are guaranteed that an object is of a given type, so:

  • there is no need to spend time and resources investigating the TYPE of a variable or parameter, because we don't know if this is a None, List[string], or just one string
  • there are no runtime type errors of this form
  • there is no worry from mistakes in the use of conventions (e.g. intPhoneNumber changed to an string, but forgot to change the name to strPhoneNumber)

There are probably other points that I have not mentioned. But the general idea is that there is less cognitive load on the programmer and if there are incorrect assumptions, feedback is immediately provided in the form of a failed compilation - and immediate feedback is exactly one of the winning factors of unit testing.

Duck-typing on the other hand does not care about the explicit type of an object - as long as the object has the relevant attributes (data members and functions), the program can go ahead. If the object has a function with the same name as what is required by some piece of code, then the code will attempt to use the function, even if the implementation is unexpectedly different. If the object does not have the required attributes, then the program crashes. The advantage of this is a reduction in code - no interface definition is necessary in a duck-typing language.

As described above, such an unexpected scenario can only be caught at runtime. But a statically-typed language would catch such a mismatch at compile time. Unless a robust unit testing suite is set up with 100% path code coverage, such a basic - and preventable - error (type mismatch) is liable to be missed. And we know that it is very difficult for a variety of reasons to achieve such a high percentage of code coverage, let alone path code coverage.

Therefore:

  • Wouldn't the 'disadvantages' of static-typing - meaning more verbose code - be far outweighed by the advantages of its intrinsic boost to software reliability?
  • From a production software reliability perspective, is there any winning advantage to using languages with such features?
  • Or is it simply that such languages shouldn't be used for production (even though they are in many, many instances)?
Was it helpful?

Solution

1. Much of what you said is just as true in statically-typed languages.

Duck-typing on the other hand does not care about the explicit type of an object - as long as the object has the relevant attributes (data members and functions), the program can go ahead. If the object has a function with the same name as what is required by some piece of code, then the code will attempt to use the function, even if the implementation is unexpectedly different.

If you write an IFoo interface, I can create a BadFoo class that derives from it and has completely wrong implementations of all the methods which cause all sorts of runtime errors inside code you wrote that relies on some IFoo instances being properly written. The compiler can't stop me from doing that. As a result, this claim:

Unless a robust unit testing suite is set up with 100% path code coverage, such a basic - and preventable - error (type mismatch) is liable to be missed.

is absolutely true in all languages, whether they're static or dynamic and whether or not they support some form of duck typing. In my experience the type-related runtime errors you do get in traditional statically-typed languages are among the most insidious errors those languages can produce.

2. Static typing and duck typing are not mutually exclusive.

In principle, a compiler can easily see that IFoo is an interface containing the method foo() and MegaCorpFoo is a class with a foo() method, and thereby conclude that MegaCorpFoo satisfies the IFoo interface without any programmers having to explicitly say so.

To the best of my knowledge, Go is the only language that clearly does this. C++ templates arguably accomplish it, but in a fragile and cryptic manner. You can probably also hack up an equivalent of this in Java or C# with their reflection APIs.

3. Traditional statically typed languages do have type-related runtime errors

C++, Java and C# all allow various forms of casting, many of which are inherently "unsafe" in the sense that they can lead to runtime errors.


The actual difference between static typing (in traditional OOP languages) and duck typing is not that duck typing is less safe, but rather that MyFoo has to explicitly declare that it satisfies the IFoo interface, and there has to be an IFoo interface explicitly defined somewhere to begin with.

So the real tradeoff is not necessarily one of safety, but simply of verbosity.

The benefit of this verbosity is that because the compiler gets more detailed instructions from me, it can check more things for me at compile time. This also tends to enable more powerful static analysis tools that can find memory leaks and race conditions and other subtle problems you might never discover on your own.

The downside is that those more detailed instructions distract from the "real code" that implements the logic you actually care about, making it a tiny bit harder to understand and perhaps easier to break when people have to change it later. A stricter type system is also just as capable of getting in your way as it is of helping you, such as when you have to write a wrapper class around some third-party class just so you can add a declaration that it satisfies an interface the third party had no way of knowing about.

My personal opinion is that an application which has to change extremely frequently and has lots of dependencies (like many web apps) is often better off with less verbose languages where the programmer making the changes can focus solely on getting the "real code" to do the right thing and pass a bunch of tests, instead of fighting the type system on corner cases that will likely never arise before the code has to get changed again. On the other hand, an application that should rarely change, has few dependencies and needs to be extremely reliable and consistent over long periods of time (such as a database or a web server) benefits more from taking the time to explicitly define all of its types and to get more compile-time checks and static analysis compiler verify that many classes of bugs are provably impossible.

Ideally, we'll eventually come up with a language that combines static typing as the default with more verbosity-reducing features like duck typing in such a way that you get the best of both worlds and this whole debate becomes a non-issue.

OTHER TIPS

You have an incorrect assumption in there. Duck typing can be statically checked. Since the compiler knows what type is being passed into the function and it knows what operations that function needs, it can detect errors when there is a mismatch.

Likewise, your second bullet is a little optimistic. Languages like Java have type casting, meaning you can get runtime errors due to trying to do operations on stuff that doesn't support them.

All that said, the argument for languages without type annotations is that they are easier to read. You don't have the "noise" of the type annotations cluttering your code, so you can read what is actually going on. Likewise, for dynamic languages an argument made is that you're going to have the unit tests anyways, so that is not a undue burden.

Personally, I disagree with those arguments but my needs are different from others'.

Licensed under: CC-BY-SA with attribution
scroll top