I Am Hate Method Overloading (And So Can You!)

My hatred of method overloading has become a running joke at Guidewire. My hatred is genuine, icy hot, and unquenchable. Let me explain why.

First Principals
First of all, just think about naming in the abstract. Things should have good names. A good name is unique and easy to understand. If you have method overloading, the name of a method is no longer unique. Instead, the real name of the method is the sane, human chosen name, plus the fully qualified name of each argument’s type. Doesn’t that just seem sort of insane? If you are writing a tool that needs to refer to methods, or if you are just trying to look up a method reflectively, you have to know the name, plus all the argument types. And you have to know this even if the method isn’t overloaded: you pay the price for this feature even when it isn’t used.

Maybe that strikes you as a bit philosophical. People use method overloading in java, so there must be some uses for it. I’ll grant that, but there are better tools to address those problems.

In the code I work with day to day, I see method overloading primarily used in two situations:

Telescoping Methods
You may have a function that takes some number of arguments. The last few arguments may not be all that important, and most users would be annoyed in having to figure out what to pass into them. So you create a few more methods with the same name and fewer arguments, which call through to the “master” method. I’ve seen cases where we have five different versions of a method with varying numbers of arguments.

So how do I propose people deal with this situation without overloading? It turns out to be a solved problem: default arguments. We are (probably) going to support these in the Diamond release of GScript:

  function emailSomeone( address:String, subject:String, body:String,
                         cc:String=null, logToServer:boolean=false, 
                         html:boolean = false ) {
    // a very well done implementation
  }

A much cleaner solution. One method, with obvious syntax and, if your IDE is any good, it will let you know what the default values of the option arguments are (unlike method overloading.)

True Overloading
Sometimes you truly want a method to take two different types. A good example of this is the XMLNode.parse() method, which can take a String or a File or an InputStream.

I actually would probably argue with you on this one. I don’t think three separate parse methods named parseString(), parseFile() and parseInputStream() would be a bad thing. Code completion is going to make it obvious which one to pick and, really, picking a unique name isn’t going to kill you.

But fine, you insist that I’m a terrible API designer and you *must* have one method. OK, then use a union type (also probably available in the Diamond release of GScript):

  function parse( src:(String|File|IOStream) ) : XMLNode {
    if( src typeis String ) {
      // parse the string
    }
    ...
  }

A union type lets you say “this argument is this type or that type.” It’s then up to you to distinguish between them at runtime.

You will probably object that this syntax is moderately annoying, but I’d counter that it will end up being fewer lines of code than if you used method overloading and that, if you really want a single function to handle three different types, you should deal with the consequences. If it bothers you too much, just pick unique names for the methods!

So?
Let’s say you accept my alternatives to the above uses of method overloading. You might still wonder why I hate it. After all, it’s just a feature and a pretty common one at that. Why throw it out?

To understand why I’d like to throw it out, you have to understand a bit about how the GScript parser works. As you probably know, GScript makes heavy use of type inference to help developers avoid the boilerplate you find in most statically typed languages.

For example, you might have the following code:

  var lstOfNums = {1, 2, 3}
  var total = 0
  lstOfNums.each( \ i -> { total = total + i  } )

In the code above, we are passing a block into the each() method on List, and using it to sum up all the numbers in the list. ‘i‘ is the parameter to the block, and we infer it’s type to ‘int’ based on the type of the list.

This sort of inference is very useful, and it takes advantage of context sensitive parsing: we can parse the block expression because we know the type of argument that each() expects.

Now, it turns out that method overloading makes this context sensitive parsing difficult because it means that when you are parsing an expression there is no guarantee that there is a single context type. You have to accept that there may be multiple types in context when parsing any expression.

Let me explain that a bit more. Say you have two methods:

  function foo( i : int ) {
  }
  
  function foo( i : String ) {
  }

and you are attempting to parse this expression:

  foo( someVar )

What type can we infer that the context type is when we parse the expression someVar? Well, there isn’t any single context type. It might be an int or it might be a String. That isn’t a big deal here, but it becomes a big deal if the methods took blocks, or enums or any other place where GScript does context type sensitive parsing. You end up having lists of context types rather than a single context type in all of your expression parsing code. Ugly.

Furthermore, when you have method overloading, you have to score method invocations. If there is more than one version of a method, and you are parsing a method invocation, you don’t know which version of the method you are calling until after you’ve parsed all the arguments. So you’ve got to run through all the argument types and see which one is the “best” match. This ends up being some really complicated code.

Complexity Kills

Bitch, bitch, moan, moan. Just make it work, you say. If the java guys can do it, why can’t you? Well, we have made it work (for the most part.) But there’s a real price we pay for it.

I’m a Berkeley, worse-is-better sort of guy. I think that simplicity of design is the most important thing. I can’t tell you how much more complicated method overloading makes the implementation of the GScript parser. Parsing expressions, parsing arguments, assignability testing, etc. It bleeds throughout the entire parser, its little tentacles of complexity touching places you would never expect. If you come across a particularly nasty part of the parser, it’s a good bet that it’s there either because of method overloading or, at least, is made more complicated by it.

Oh, man up! you say. That’s the parser’s and tool developer’s problem, not yours.

Nope. It’s your problem too. Like Josh Bloch says, projects have a complexity budget. When we blow a big chunk of that budget on an idiotic feature like method overloading, that means we can’t spend it on other, better stuff.

Unfortunately, because GScript is Java compatible, we simply can’t remove support for method overloading. If we could though, GScript would have other, better features and, more importantly, fewer bugs.

That is why I am hate method overloading. And so can you.


It can always be better

George Bernard Shaw put it best when he remarked that all progress depends on the unreasonable man.  Progress in just about any area requires that you not be content with the status quo, since otherwise you wouldn’t bother trying to make it better.  But at the same time, constant discontent with the world is a rough way to live; if nothing is ever good enough, you’ll always be unhappy, and if you’re always going to be unhappy what exactly is the point of working so hard to make things better?  On a pretty fundamental level, isn’t that drive to make progress and improve things the result of a desire to turn some discontentment with the current state of affairs into contentment?  But once you reach that contented state, how do you keep going?  Shaw could just as easily have said that all progress depends on the discontented man, but who wants to spend their live discontented?

Dealing with that requires finding a balance between contentment and complacency on one hand and discontentment and action on the other hand.  Logically, they’re kind of two ends of one scale; they’re P and not-P.  You can only have one, right?  Thankfully (or not, depending on your perspective), the human brain isn’t actually constrained by the rules of logic.  Logic tells us “P or not-P” but your brain says, “Why can’t I have both again?”  Paradoxes are a part of life, and they’re not just the result of lazy thinking; as much as the analytic philosopher/computer scientist in me wants the world to be a rational, P or not-P sort of place, it’s not, and we can find ways to use that to our advantage.  

My solution is to take that one single axis of “am I happy with X” and treat it as if it’s actually two independent axes; I call them “contentment” and “satisfaction” for lack of better terms, such that I can be content while still not being satisfied with things.  If I’m running, I can simultaneously be contented that I ran what for me is a good time while still being dissatisfied that I didn’t set a new personal best.  And if I’m working, I can simultaneously be extremely happy with how much I’ve been able to do while being appalled that I haven’t done more, and I can be content with the state of our code and our tools and how advanced they are compared to what our competitors try to work with while still feeling like I won’t actually be happy until they’ve come miles further than where they are now.

This is just my opinion, of course, but I think a lot of people tend to fall into just one sort of thinking or the other:  either they’re happy enough with things that it reduces their drive to improve them, or they’re driven to improve things but they’re unhappy (and in many cases, that can lead to feelings of futility, which eventually just kills motivation altogether).  I’m not sure I really have the balance mastered myself, but I’m pretty convinced that it’s critical to doing the best work that I can do and avoiding complacency on one hand and burnout on the other.

There’s a more sinister form of the discontentment that afflicts software engineers as well, and that’s the gold-plating/no shipping afflication that leads to never deciding something is good enough to move on.  So if you’re an engineer, that’s yet another potential affliction to master; you need to find a way to be content and enjoy your work while not getting burned out, and to constantly improve everything while still being able to ship code and move on to new projects.

If there’s one thing I look for in engineering candidates these days, it’s probably that constant drive to improve things, and that recognition that things can always be better.  Because they can.  There’s no end-point in the game we’re playing (yes, even if you’re a Lisp guy, the answer is still out there); the language, the tools, the libraries, the techniques, the tests, the code, the process . . . no part of the stack is ever truly done.  To me one of the joys and truly unique things about software development is the fact that there is always progress to be made, and that every engineer has the opportunity to make that kind of progress.  I think that constant drive is something that all truly great engineers share.  Be unreasonable.

But even better than that constant drive to make things better is the ability to keep that drive alive over months and years and decades while staying happy, enjoying the work, avoiding burnout, and being able to priortize that against the need to ship and the need to move on to other projects.