Erik
10/16/2022, 3:09 PM@Composable
fun MyComposable(vararg composables: @Composable () -> Unit) { /* impl */ }
Now I want to let the caller pass some styling also, so I refactor it to:
@Composable
fun MyComposable(
attrs: AttrBuilderContext<HTMLDivElement>? = null,
vararg composables: @Composable () -> Unit
) { /* impl using a div that applies attrs */
}
Now all my code no longer compiles, because wherever I used to call e.g.
MyComposable(
{ /* Some composable */ },
{ /* Some composable */ },
)
now suddenly the first lambda is used as the attrs
parameter! It doesn't compile, luckily, because it's not allowed to call a @Composable
function from a non-composable context: the AttrBuilderContext
happens to be a lambda that @Composable () -> Unit
fits into... 🤦♂️ A coincidence, but one that will likely happen to more users.
The workaround beats the purpose of using a lambda in the first place:
MyComposable(
composables = arrayOf(
{ /* Some composable */ },
{ /* Some composable */ },
)
)
This works, and is not nice. See also this thread.Erik
10/16/2022, 3:14 PM@Composable
fun MyComposable(
attrs: AttrBuilderContext<HTMLDivElement>? = null,
vararg composables: @Composable () -> Unit
) { /* impl using a div that applies attrs */
}
// Somewhere in code
MyComposable(
{ /* Some composable */ },
{ /* Some composable */ },
)
Erik
10/16/2022, 3:16 PMModifier
objects as the first parameter (with usually a default), so signature clashes likely won't happen there!nschulzke
10/16/2022, 8:27 PMnull
in explicitly or, if you don't want to break existing call sites, make an overload that has the same signature as the original function:
@Composable
fun MyComposable(
vararg composables: @Composable () -> Unit
) =
MyComposable(null, *composables)
This strategy for refactoring is one you want to hold on to for any language: if you need to refactor a function and don't want to break existing call sites, keep a version of the function that has the original signature. This is widely applicable in many situations, not just with varargs and not just in Kotlin.
As for why the idiom you wanted to use isn't supported: when you're passing in arguments positionally, you can only omit arguments at the end, never in the beginning or in the middle, with the only exception being the single trailing lambda (which is an idiom that you are not using in your examples). Making another exception here for varargs would make the language's behavior much harder to reason about and not enable any patterns that aren't easily achievable already.
(This is in contrast to the trailing lambda exception, which forms the basis for Kotlin's entire standard library.)Erik
10/16/2022, 8:39 PMErik
10/16/2022, 8:48 PM