Function default arguments have some interesting b...
# getting-started
e
Function default arguments have some interesting behaviour. E.g.: see this snippet: pl.kotl.in/38f7SMhpi Some observations and questions: • Function defaults are re-invoked on every function call without that argument. But note that, of course, the implementation of the function might return the same thing or a different thing everytime. • Literal defaults (e.g.
argument: () -> Unit = {}
) aren't evaluated more than once if the function with that default argument is called more than once. I.e., the
{}
literal lambda is only instantiated once. When is it instantiated for the first time? I assume the first time the default is actually required. And how long does the produced literal value remain in memory? Forever? Until the function is no longer referenced (e.g. on JVM garbage collection). In other words: is the literal, in fact, a singleton? This might be important to know if the default object is heavy on resources, like memory.
k
I don't find that behaviour surprising. This boils down to two things, and everything else follows: 1. By design, default parameters are evaluated every time you call a function that is missing those parameters, although the documentation is not that clear. It's as if the caller passed those same parameters explicitly in the function call. 2. Empty collection functions such as
emptyList()
return a cached collection, because they can easily do that, but on the other hand it's not easy for
emptyArray()
to return a cached empty array because arrays carry the element type with them, so to return a cached empty array would involve caching arrays of every conceivable element type, so instead
emptyArray<SomeType>()
creates a new
Array<SomeType>
.
e
That I also find unsurprising
However, what I do wonder about is what happens for literal objects, like
{}
, when used as a default. At least on JVM, that is some kind of
Function
object instance. When it is instantiated once, will it remain in memory forever? Or could the literal be evaluated a second time under some specific circumstances?
k
You get 1 instance of a lambda every time it's encountered as a literal. Consider this:
Copy code
fun main() {
    val f1: () -> Unit = {}
    val f2: () -> Unit = {}

    val fnset = mutableSetOf<() -> Unit>()
    for (i in 1..1000) {
        val fn: () -> Unit = {}
        fnset.add(fn)
    }

    check(f1 !== f2)
    check(fnset.size == 1)  // Because we added the same instance of the lambda each time
}
That just demonstrates it without using functions or default function parameters. In fact, it's the same with default parameters:
Copy code
val fnset = mutableSetOf<() -> Unit>()
fun foo(lambda: () -> Unit = {}) {
    fnset.add(lambda)
}

fun main() {
    for (i in 1..1000) {
        foo()
    }
    check(fnset.size == 1)  // Because we added the same instance of the lambda each time
}