Core Java

Flag Parameters and Overloading in Python, Java, and Kotlin

Jumping around between multiple languages can help you notice some differences between idioms and best practices in different languages. One of the more interesting differences has to do with one function doing multiple things.

Python

We’ll look at Python first. Python is actually incapable of overloading, since defining a new function/method with the same name would just overwrite the previous one. Because of this, having flag parameters (boolean, enum, or None-or-something parameters) to signal slightly different behaviors is natural and idiomatic in Python, especially with default arguments. The default arguments make it especially useful, since the flag parameters usually have a value that is used significantly more often than the other(s).

The only time this really becomes a problem is when someone calls the function and passes in just the hard-coded value. This mostly just applies to boolean flag parameters, but it can also apply to the other types of flag parameters when their name/value doesn’t automatically reveal the context. For example, a function like this:

def lookUpPerson(id, cache_result):
    # looks up the person, caching the result if cache_result is True
    return person

And then that function is called like this:

person = lookUpPerson(id, True)

When someone comes by and reads this line, they may not know or may have forgotten what the second parameter is. It’s confusing. Luckily, in Python you can use named arguments and call the function like this:

person = lookUpPerson(id, cache_result=True)

This makes the purpose of the parameter much easier to figure out. In Python 3, they made it so that you can make parameters only called as keyword arguments. This is a good idea, since these flag parameters generally should be called that way consistently. So the function should be changed to look like this:

def lookUpPerson(id, *, cache_result):
    # looks up the person, caching the result if cache_result is True
    return person

Okay, now it’s really nice, and using it will always be nice and legible.

Java

Onto Java, now. In Java, it is considered horribly bad form to use flag parameters. There are two good reasons for that: Java allows overloading, but it doesn’t allow named arguments. Without the named arguments, the previous function call (now translated to Java) will always end up like this:

Person person = repo.lookUpPerson(id, false);

It takes some real work to make it completely obvious what the second parameter is for. You could place a comment in there or create a variable that equals false where the name defines what it is. Both of these work, but the standard, best-practice way to deal with this idea in Java is to make two different methods:

public Person lookUpPerson(int id) {
    // looks up the person
    return person;
}

public Person lookUpAndCachePerson(int id){
    // looks up and caches the person
    return person
}

This can be done in Python, but it’s not usually the idiomatic way. The nice thing about this is that it’s more explicit about what it does and how it works. The bad thing is that it often gets a little long-winded, especially once you escalate the problem by adding more flags.

Mixing It Up

Personally, I agree with both opinions, as they’re both great solutions in their own languages. There’s good reason for them to be idiomatic where they are. But I’d like to extend Python’s idiom a little bit.

The problem with Python’s way of doing it is that the function, by definition, is doing more than one thing, since it does one thing sometimes and another thing other times. I’d like to change the idiom just a little bit to follow SRP (Single Responsibility Principle) a little better.

You get to keep the current function signature as-is, but the implementation is changed and a couple more functions pop up.

def lookUpPerson(id, cache_result):
    if cache_result:
        return lookUpAndCachePerson(id)
    else:
        return simplePersonLookup(id)

def lookUpAndCachePerson(id):
    # looks up and caches person
    # probably uses the next function for doing the lookup
    return person

def simpleLookUpPerson(id):
    # looks up the person
    return person

What does this give us? As stated earlier, it makes the code follow the SRP better; lookUpPerson() only has the responsibility of choosing which of the more refined functions to call. The other two functions are honed down in responsibility, although lookUpAndCachePerson() clearly has two responsibilities, which you can see by reading its name. But caching is really an under-the-hood side effect and it overall probably doesn’t make for the best example for my point, and I’m too busy to try to think up something different :)

Not only does this give us a better-factored piece of code, it also gives the user some code options that can be clearer in certain situations. The user can call the original function, possibly even having the keyword argument being dynamically provided, or it can make it clear whether it is or isn’t using the cache by calling one of the branched functions.

What About Kotlin?

Finally, we get to Kotlin. Kotlin is an interesting beast, and being a new language (still not even on version 1), some parts of it don’t have idiomatic uses yet, and this is kind of one of those undefined idioms so far. Kotlin has the ability to give keyword arguments and has default parameters, but it doesn’t have the ability to force a parameter to be a keyword argument. Also, it does support overloading. But the biggest factor for all of this is the fact that Kotlin is fully interoperable with Java, which will not be able to use the keyword arguments.

The biggest reason I brought up the slightly altered idiom for Python wasn’t as much to present a change we should be making to Python, as much as I would like to see that be the case, but it was more to introduce what I think should be the idiom in Kotlin. Any time that someone creates a function or method in Kotlin that has default parameters, additional methods should be created, preferably public ones.

Why is that, besides the reasons given for why Python should? Due to the fact that Kotlin code is meant to also be callable from Java code, and Kotlin functions with default parameters are just functions with the full parameter list to Java, we should write Kotlin code in a way that doesn’t throw off users in Java. Although, if you were sure that your code would only be used by Kotlin code, I would be much more lenient about following this set of advice.

There’s something to keep in mind in Kotlin, though: you should avoid using overloading for providing the other functions. To understand why, let me show you an example:

fun aFunction(x: Int, y: Int = 5): Int = x + y
fun aFunction(x: Int): Int = aFunction(x, 5)

With these two functions defined, the second being provided so that Java users can have a version with “default” values, what happens when you make this call:

z = aFunction(2)

You get a compiler error. It is ambiguous as to which function is being called. The same thing happens when you’re trying to pass the function for a `(Int) -> Int` parameter. So avoid this problem and make your secondary methods have different names than the one with defaults.

Outro

That’s really all I have this week. I’d really like to hear some opinions about this, especially with good examples to back up criticism. This was just some observations and some thoughts on unification. Now it’s time to get back to writing my book. Thanks for reading!

Jacob Zimmerman

Jacob is a certified Java programmer (level 1) and Python enthusiast. He loves to solve large problems with programming and considers himself pretty good at design.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button