Python Annoyances: Followup
I’ve gotten a fair bit of response to my list of minor complaints with Python. Many of those have been quite helpful, and I’ve learned some good tricks that have definitely made things easier on me. Thank you.
I’ve also gotten a lot of “you need help with switching to dynamic language” responses, however, and those frankly are pissing me off.
Let’s take the duck typing example that some dynamic language designers love to use as “proof” that dynamic typing is so awesome in all cases ever. Sure, “if it walks like a duck and quacks like a duck, I’d call it a duck.” That’s fine and dandy. But how the hell do you actually find out of if it quacks like a duck before you try to make it quack? Well, you just try it and then watch exceptions start flying when you try to tell a toaster to quack, apparently. Fun.
I don’t want rigid typing everywhere. I don’t need rigid typing everywhere. I do have some code that simply isn’t going to work if you pass a string, or a number, or None, or a list,
I think the problem here is that there are very, very few languages that have any kind of support for type checking based on arbitrary constraints instead of on rigid types. Java makes you use interfaces. Python just makes you hope and pray. Where the hell is the middle line?
C++0x has a pretty neat feature called Concepts. A Concept is a list of constraints for a type. For example, you might have a Duck Concept that requires that the type have a walk method and a quack method. You can then state that a generic method (which is C++’s way of doing sorta-dynamic typing) parameter must adhere to a particular Concept (or multiple Concepts). So — just like Python — you can pass ANY type you want to such a method without having to inherit from any base class; you never have to tell C++ that the type adheres to the Duck protocol. The language does the Concept tests and figures it out for you. That’s damn handy.
Imagine if in Python you could define a small set of Concepts for your module, or use built-in ones. You can say that method foo takes a “mutable container.” You could pass in a hash, a list, or an object that behaves like a container, just like you already can. If you pass in a string, an int, None, or something else, you get a nice, clear exception that tells you right up front that you passed in the wrong type (and what the right type is) instead of getting some cryptic message about attempting an array operation on some value passed to an inner function called by another function that was called by the actual public API function you originally called in the module. Oh, that would be ever so nice. And, of course, it would be optional.
I mentioned Boo before, and I think it has the right idea for variables. It’s a statically typed language that offers optional dynamic facilities. The static typing stays out of your face so you don’t have to type out huge type specifiers all over the place over and over again. Plus it has a Python-like syntax. If it wasn’t for the political troubles Mono faces with getting into various distributions, I’d probably be using that instead of Boo already.
The general idea with Boo is that, once you define a variable, that variable’s type is also static. So the following code would fail:
foo = 42
foo = “answer”
Once you declare var as an int (implicitly), var is statically typed to int. Seriously, most of the time this just isn’t an issue, because very little code is going to change a variable’s type in a single block. There are exceptions, sure, but they’re pretty darn rare in comparison.
Boo also has a duck typing facility, which allows you to declare a variable as duck (it would have been a little less goofy to call it “mixed” or “var” or something, IMO), which then allows that variable to behave just like you’d expect of any other dynamic language. You can even set Boo to use duck typing by default for variables instead of using type inference, if you so wish.
ECMAScript 4 has the opposite, in which a variable is dynamic by default but can be optionally constrained. (Without type inference, unfortunately.) So you can do:
var foo = “banjo”; // duck typing
var foo as int = 64; // static typing
So, the Python apologists can see that there are plenty of options for dynamic languages to offer static type checking facilities for those cases where it’s just plain useful, while still allowing dynamic behavior elsewhere. To put down my issues with Python purely as “problems adjusting to using a dynamic language” are bunk. Wanting the language to help me enforce the rules I know my software needs is not in any way at odds with dynamic typing. To hear Python folks say that wanting any kind of type checking is “not the Python way” is just as silly to my ears as hearing the PHP folks say that having named function parameters is “not the PHP way.” Yeah. Old versions of the language didn’t have it as the language simply hadn’t matured enough yet, so now you will decide to continue to not have the feature in perpetuity due to some indefinable criteria about “the way” code should be written in the language. If I have to hack around a language’s feature set in order to save myself time or make my code more maintainable, the proper response is not to stammer out apologies or sling accusations about me “doin’ it rong.”
That out of the way, I am again thankful for the help I’ve received. It’s been years since I’ve last used Python for anything substantial and trying to relearn some of the Python peculiarities or the new features that didn’t exist all those years ago has been slower going than I’d have hoped. Thank you!