Elegant Local Function

1 minute read

What exactly is local function? It was new to me in Kotlin and thought it is interesting to dig deeper. In a normal class, we have many functions within a class - the functions have names so that we know which functions are linked to each other.

Local functions are the functions with names that are defined within other functions.

So what is it for? Well, local functions reduce duplication by extracting the repetitive code. Moreover, they are only visible within the surrounding function, so they don’t mess up your code readability.

Let’s see an example :

fun doPrint() {
  val buffer = StringBuilder()

  fun printWarning(message: String) =
    buffer.appendLine("Warning: $message")

  printWarning("Your attention please")
  val num = 123
  printWarning("Do not use the line number: $num")

  val result = buffer.toString()
  // Warning: Your attention please
  // Warning: Do not use the line number: 123
}

Notice that printWarning() has access to buffer - because local functions are closures, so you don’t have to pass additional parameters.

With Function Reference

You can refer to a local function using a function reference:

class Event(
  val title: String,
  val location: String
)
events = listOf(
  Event("Kotlin", "Room A"),
  Event("Swift", "Room B")
)

fun mustAttend(event: Event): Boolean {
  if (event.title.contains("Kotlin") {
    return true
  }
  return false
}

val result = events.any(::mustAttend)
// true

With anonymous function

The function mustAttend() is only used once, so you might inclined to define as a lambda. An alternative is using anonymous function.

Anonymous functions are defined within other functions - but no name. They are similar to lambdas, but use the fun keyword.

events.any(
  fun(event: Event): Boolean {
    if (event.title.contains("Kotlin") {
      return true
    }
    return false
  }
)

Summary

If a lambda becomes too complicated and hard to read, replace it with a local function or an anonymous function.

fun usingAnonymous: (Int) -> Int {
  val func = fun(i:Int) = i + 1
  val result = func(1) // 2
}
fun usingLambda(): (String) -> String {
  val func2 = { s: String -> "$s!" }
  return func2("abc")
}
fun usingRef(): () -> String {
  fun greet() = "Hi!"
  return ::greet
}
fun usingRefCompact() = fun() = "Hi!"
fun usingLambdaCompact() = { "Hi!" }