Chiming in on Duck Typing vs Static Typing

Crowley and Malone have been going at it over duck typing vs static typing. I’ve decided to invite myself into the discussion. Crowley’s initial post is here. Malone’s response is here (read the comments as well) and Crowley’s follow up is here.

For static typing

Richard’s argument is that duck typing does not alert the developer that the wrong argument type had been supplied to a method until it is too late, if at all. His two examples are 1. passing a CreditCard to a template instead of a Kitten, resulting in the CreditCard’s number being displayed where a Kitten’s name should have shown up and 2. more interestingly, a method which does something scary up front (presumably, by ‘poke_a_tiger_with_a_stick()’ Richard means a persistent data write or a financial transaction), then iterates over one of its arguments and fails if the argument passed is not an iterable. Richard would rather know that the argument passed is not of the right type up front, so that the method would fail immediately. He suggests “turkey typing,” a C++ STL-like syntax for telling the compiler what the argument to a function should be. In his example, he uses an iterable containing only certain types of objects.

For duck typing

Mike’s argument is that duck typing provides much more flexibility than static typing – if you need an object to only fullfill one part of a certain interface, you can simply define that part of the interface, plug the object into a method that expects that specific part of the interface, and be on your merry way. Further, Malone wants exactly what Richard doesn’t – to be able to pile all sorts of different types of objects into a collection. Another important point he brings up in the comments is that duck typing allows many different developers to come up with many different solutions to a problem defined by an interface without having to share any common ground at all. As long as the same interfaces are implemented, the objects can be swapped in and out. Free-flyin polymorphism.

Does “Turkey Typing” work?

Richard’s proposal is to specify the type of argument up front – use some syntax to say that an argument should only be of a certain type or an iterable with only certain types in it, and everything is peachy: if you pass the wrong type, the compiler yells at you before you get to any logic.

However, this lacks flexibility in the same way that static typing does. It effectively encourages the creation of a whitelist for what sorts of things should be allowed into the method. Interfaces (the language constructs) can mitigate most of this, but only if the developer of the method uses an interface – extra boilerplate code.

Let’s take Flickr as an example. Initially, users could only upload photos. Using turkey typing, we’d have functions that would expect an object of type Photo. Then Flickr added video. We would have to go through all the functions that deal with generic actions – uploading, describing, deleting, etc – and edit the signature to also include Video objects. Of course, we could create a superclass, but then all of a sudden this doesn’t seem very different from the statically typed world Malone describes – lots of useless code just to be able to do simple things.

Both sides are interested in the same thing – an object’s capabilities. Malone is fine either A. assuming that people know what they’re doing or B. checking the capabilities manually. Crowley wants that check to be contained in the signature. The problem is, he wants to discover the capabilities by looking at a name. It’s equivalent to using the UserAgent string of a browser to try to figure out what code it can and can’t handle. It just doesn’t go well. If Richard’s syntax is to accomplish what it’s really after, it’ll have to become so complicated, that it’s basically not worth the trouble.

The tiger argument

Let’s turn Richard’s example into something more realistic for a second. Say you have a function called purchase_items(), which takes as arguments a user object an array of items, puts those items into a shipping queue, and charges the user’s credit card. poke_a_tiger_with_a_stick() in this case is charging the credit card, and the loop that follows is the insertion of items into a shipping queue.

Richard is right to say that if you charge the credit card, and then the rest of the function fails because some idiot dev you hired straight out of college passed something stupid instead of the array of items (maybe a single item), your customer will be pretty pissed.

The problem with this thought experiment is that it uses shitty coding as its premise. If you charge the credit card before you’re sure you’ve verified your data and successfully recorded the order, you deserve that angry support phonecall. You should always save the critical transactions for for last, and I know that Richard knows this. You’ll never see him poking a tiger with a stick as the first order of business. Simply restructuring the method would surface the problem with the argument immediately.

Where I stand

I think Malone’s assessment is correct: static typing and permutations thereof ultimately aim at saving people from doing stupid things. However, there is plenty of software out there written in statically typed languages that does stupid things. You can’t help stupid. You can, however, write your code in a way that minimizes its impact, but you don’t need typing for that, just common sense.

Meanwhile, loosely typed languages allow allow for great flexibility and efficiency.

Biases

I think it’s worth noting that Crowley spends most of his time writing C and C++, whereas Malone and I work mostly in Python and PHP, respectively. I’m pretty sure that all of our preferences just line up with what we’re comfortable with.

4 Responses to “Chiming in on Duck Typing vs Static Typing”

  1. David Hall says:

    What I don’t get in people’s criticisms of Richard is that they assume you have to specify a type. I don’t see why we can’t have the ability to specify types in methods if we want to, and get duck typing if we don’t do it.

    Where python really fails is that if you want this strictness, it makes it a horrible pain to achieve, if not impossible. And while I consider Richard’s syntax examples ugly, they’re far prettier than what someone has to do in python.

    Your Photo/Video example is semi-silly. You know that even in these “generic” actions, the first time they were coded, they likely have Photo-specific code in them. So, you need to go through them and fix that when you decide you’re going to do video. That’s just a given. And if it truly generic, Richard’s Turkey-typing does not require superclasses, just you to specify [Photo,Video] instead of Photo, once you verify the method is not Photo-specific.

    I tend to fall on Richard’s side on this one, but maybe that’s because I live in a world where sometimes the people you write code with weren’t hired for their coding, but instead for their knowledge of chemistry, and it just so happens that they need to write some code to try out some ideas, and you want it to be in an easy dynamic language, but you find them running into things like what Richard discusses in his posts. And C has different problems. I feel like Richard’s ideas would result in a better Python ecosystem, where there are clearer error messages instead of some deep backtrace of an uncaught exception (often from a C extension when you’re in the world of scientific computing, which means the deepest levels of the backtrace are not in Python and not your standard ValueError type problems).

  2. mihasya says:

    I do see exactly what he’s trying to do – an optional “type hint” in method signatures. All of my points are aimed at that argument.

    Richard’s syntax is fine if all you’re interested in is the type. However, he is interested in the capabilities. So the part that didn’t make it into his post is that you’d have to define an interface-like structure somewhere to say what “iterable” means – what methods it has to support in order to satisfy the requirement. Then you get to the part where you also have to define the signatures for all those methods. If those methods have turkey type requirements, you have to define all of those subtypes somewhere too. To me, it seems easier to just do (pseudo)

    if (foo.iter):
        for bar in foo:
            if isinstance(bar, SomeClass):
                bar.DoEeeeet()
    

    I’ll concede that I’ve presented another weak example after pointing out weaknesses in Richards. Your point is well taken.

    The more I think about it, the more I don’t see why this has to be a syntax change at all. I believe what Richard wants can be accomplished using some pretty generic helpers. Maybe I’ll talk to him about that possibility.

  3. Adam says:

    Turkey typing seems to be exactly like ocaml and the ml family of languages ‘type hinting’ (I don’t really know what its called). Its not new, and not having comments do suck.

  4. I know I’m way late to this party, but I think the term Adam is looking for is “type inference” which is the ability of the compiler/interpreter to deduce what type you meant instead of the programmer explicitly typing it out.

    Personally, I’m in the Richard camp, loving the extra checking the compiler gives me though I hate typing out instantiations like “Foo foo = new Foo()” that languages with little to no type inference (such as Java and C++) force you to do. C++ and Java both have terrible type systems, giving you all the pain of the extra typing with only really helping the developer at runtime at the subtle type displacements. What these languages missing are type inference.

    Haskell, for example, has a fantastic type system. You specify what types a function takes and returns and if you violate this compact, either through function calls or returns, the compilation fails. This forces you to reason strongly about your program and the common thought in Haskell is if it compiles, it works. This even also allows Haskell to do crazy things like automatically generating testing suites–the type system is that strong. Even better, you don’t have t0 tell the compiler what type every value has, it figures it out for itself through its inference.

    With disciplined developers and rigorous testing, (obviously) dynamic languages work. The issue with dynamic languages is that the developers have to be disciplined and the checks they have to put in to deal with the “what is this” issues is simply and elegantly handled with strongly, statically typed languages with good type inference.

Leave a Reply