Scope functions (let, also, apply, run itd.) są bardzo uniwersalne i można je stosować w aż tak wielu różnych miejscach, że nachodzi pokusa, żeby stosować je niemalże wszędzie gdzie się da, eliminując potrzebę deklarowania zmiennych w kodzie prawie do zera (poza parametrami funkcji, o ile to zaliczamy je do zmiennych) oraz stosując wyłącznie "single-expression functions". Czy w Kotlinie warto zawsze i wszędzie używać scope functions? A może warto ograniczyć ich użycie maksymalnie i stosować np. tylko typowe konstrukcje typu ?.let{ ... }? Zgaduję, że prawda leży gdzieś po środku. Jakie jest wasze zdanie na ten temat? Czy ich stosowanie zwiększa, czy zmniejsza przejrzystość kodu? Czy ułatwia czy utrudnia rozwój? Czy wpływa na niezawodność kodu?
Poniżej kilka przykładowych fragmentów kodu, żebyśmy mogli porozmawiać o tym samym. Proszę nie doszukiwać się w nich jakiegoś głębszego sensu, ani nie sprawdzać, czy w ogóle się skompilują. Chciałbym, żeby uwaga skupiła się na scope functions, a nie na tym, czy w kodzie nie ma błędu.
fun foo() = boolQuery().also { builder ->
buildFooQuery()
.takeIf { builder.should().isNotEmpty() }
?.let { builder.must(it) }
}
fun bar(phrase: String) = boolQuery().apply {
minimumShouldMatch(1)
buildFullTextQueries(phrase).forEach {
should(it)
}
}
fun createSomethings(field: Field): List<Something> =
getSomethingValue()
.let { it as? List<*> }
?.let { buildSomethingComponents(it, somethingMetadata) }
?: emptyList()
fun createXyz(fields: List<Field>) =
somethingBuilder
.getBuilderForFields(fields)
.let { buildSomethingComponent(it) }?.attributes
?.let { filterSomethingAttributes(fields, it) }
fun filterSomethingAttributes(
fields: Map<Xyz, Something>,
somethingAttributes: MutableMap<String, SomethingAttributes>
) = xyzFields
.mapNotNull { fields[it] }
.map { it.name() }
.let { name ->
if (xyzFields.isNotEmpty())
somethingAttributes.filterKeys { name.contains(it) }
else somethingAttributes
}
fun foo2() = doDoSomething().also{ logSomething("xx: $it") }
fun foo3() = doDoSomething().apply{ build() }.also{ logSomething("xx: $it") }
W ramach ćwiczenia napisałem w takim stylu ok. 1k linijek kodu nie używając ani val, ani var, ani klasycznych funkcji z blokiem kodu, a jedynie "single-expression function". Mam kilka swoich przemyśleń na ten temat, m. in. jaką przewagę ma taki styl pisania, ale wolałbym się tym nie dzielić w pierwszym poście. Czy ten styl można podsumować dwoma słowami: "programowanie funkcyjne"?