Question: Why is 'stacked parameters' behavior not documented?
Considering the following code:
interface Greeter {
fun greet()
}
class GreeterA(private val delegate: Greeter) : Greeter by delegate
class GreeterB(private val config: Config) : Greeter {
override fun greet() = println("Hello, ${config.name}!")
}
class Config(val name: String)
fun main() {
val koinApp = startKoin {
printLogger(Level.DEBUG)
modules(
module {
factoryOf(::GreeterA)
factoryOf(::GreeterB) bind Greeter::class
}
)
}
val a = koinApp.koin.get<GreeterA>(
parameters = { parametersOf(Config("Koin")) }
)
a.greet() // prints "Hello, Koin!"
}
If someone relied only on the documentation, they might expect this code not to work, but it actually does work, and prints the expected result.
From my (non-expert) understanding of Koin internals, this works because when Koin resolves GreeterA and needs an instance of GreeterB (which is bound to the Greeter interface), it:
- First checks for parameters explicitly passed to
GreeterBfactory via theparameterslambda. - If none are found, it falls back to the current stacked parameters (I believe that's how it's called?), in this case, the parameters passed to
GreeterA. - Finds the
Configparameter there and uses it to resolveGreeterB.
This is a really cool behavior, but I couldn’t find any mention of it in the documentation.
So my question is: Is this considered an internal implementation detail (and therefore undocumented because maybe it’s not an encouraged usage), or is there another reason?
Thank you!
This sounds really dangerous and unintended
I agree. The behaviour isn't well documented and has a confusing choice of ordering
private fun <T> resolveFromContextOrNull(scope : Scope, instanceContext: ResolutionContext, lookupParent : Boolean = true): T? {
return resolveFromInjectedParameters(instanceContext)
?: resolveFromRegistry(scope,instanceContext)
?: resolveFromStackedParameters(scope,instanceContext)
?: resolveFromScopeSource(scope,instanceContext)
?: resolveFromScopeArchetype(scope,instanceContext)
?: if (lookupParent) resolveFromParentScopes(scope,instanceContext) else null
?: resolveInExtensions(scope,instanceContext)
}
I would expect Stacked Parameters (passed as parametersOf()) to be used before Registry is used on other dependencies. But I guess you then need to be more explicit when passing param down the dependency graph.