David Kubecka
04/05/2023, 11:34 AMcomputeIt().let { if (condition()) it.enhance() else it }
which would benefit from having dedicated letIf
construct? Am I missing something or it's just one of the many cases which just isn't worth adding to the stdlib?Riccardo Lippolis
04/05/2023, 11:40 AM.filterIf(condition) { ... }
on a collection a few times. And there are most likely more candidates, so why implement it for one if not for the other? Which leads to multiple needed functions which can also indeed be easily created by yourself when needed ๐David Kubecka
04/05/2023, 11:48 AMRiccardo Lippolis
04/05/2023, 11:52 AMalsoIf
, someone else will ask for filterIf
(maybe me ๐), next thing you know, everytime a library function is added we would need to consider the libraryFunctionIf
variant, which is a pollution IMHO. Especially since it's very easy to create for yourself when needed. ๐David Kubecka
04/05/2023, 11:54 AMAdam S
04/05/2023, 11:54 AMit
is unused :)David Kubecka
04/05/2023, 11:55 AMAdam S
04/05/2023, 11:55 AMalso
is never used, since it returns UnitRiccardo Lippolis
04/05/2023, 11:57 AM.let
instead of .also
indeed ๐Adam S
04/05/2023, 11:57 AMDavid Kubecka
04/05/2023, 11:58 AMrun
in my particular case ๐ )Adam S
04/05/2023, 12:00 PMRiccardo Lippolis
04/05/2023, 12:02 PMorNull
, true, and probably one of the Kotlin stdlib developers can provide the correct arguments here, but my assumption here is that they are added because of the explicit null-safety features/guarantees provided by the language.
So if the stdlib would only provide the non-nullable variants, developers would need to catch exceptions for the null/empty collection variants, and if only the orNull
variants were present, developers would need to explicitly cast to non-nullable types wherever they actually know it will never be null.
but in general, it's indeed just a matter of where to draw the line. I agree with you that it would be nice to have the ...If
variants present in the stdlib, but I'm not the one needing to maintain them ๐Adam S
04/05/2023, 12:19 PM..If
variants would open the door to a similar mess? Itโs probably okay using one letIf() {}
, but what happens when lots of them are chained, or nested? And combined with the (imo) already confusing scope functions? I suspect it wonโt be very legible, and just using an if-statement (or when-statement) would be better - but thatโs just a gut feeling.David Kubecka
04/05/2023, 12:39 PMletIf
even more because for my way of thinking it's much more natural to chain the conditions then combine them. E.g. I would rather have
value.letIf(cond1) { ... }.letIf(cond2) ...
then
value.let {
val uselessIntermediateVal = if (cond1) {
...
} else {
it
}
if (cond2) {
...
}
}
Joffrey
04/05/2023, 12:48 PMlet
. What is your real-life use case here?Adam S
04/05/2023, 12:56 PMvalue.letIf(cond1) { ... }.letIf(cond2) ...
example looks quite fluent!
However I would also probably prefer to make a specific function. Even a local function can help out enormously.
fun computeResult(
enhanceFooEnabled: Boolean,
enhanceBarEnabled: Boolean,
): Float {
fun Float.enhanceFoo() = if (enhanceFooEnabled) enhance() else this
fun Float.enhanceBar() = if (enhanceBarEnabled) enhance() else this
val value: Float = computeIt()
return value.enhanceFoo().enhanceBar()
}
David Kubecka
04/05/2023, 1:05 PMlet/run
which seems unnecessarily verbose, given how compact yet still clear Kotlin usally is.
I don't have a concrete example for the chained letIf
yet. That was just an extra argument for me why something like that should be in the stdlib.Joffrey
04/05/2023, 1:25 PMcomputeIt()
and a function named condition()
? My point is about semantically extracting a function out of this construct, but without real life names this is moot.
Usually something like this is more readable:
computeIt().enhanceIfNeeded()
fun TheType.enhanceIfNeeded() = if (condition()) it.enhance() else it
But maybe the code can be organized differently as well, and not even need thatDavid Kubecka
04/05/2023, 2:02 PMmergeToBalanceHistory(runSumTransactions, aggregatedBalances)
.run { if (isGroupByDate) fillMissingPeriods().applyDateFilter(dateConditions) else this }
It's IMO exactly the same pattern. I don't see how extracting to an extension function would help my main problem which is the verbose if (cond) doSomething(it) else it
pattern.
Of course, the verbosity here is not that bad after all. I was just interested how the community views this problem in particular, and the extension of stdlib in general.Joffrey
04/05/2023, 2:05 PM.run
and else this
if you don't? If you do, then this whole expression could actually be in a function using early returns, and there is no need for scope functionsDavid Kubecka
04/05/2023, 2:09 PMJoffrey
04/05/2023, 2:12 PMI think we are now getting to the territory of purely personal preferenceOf course, I agree. It seemed to me that the question is all about style anyway, so I figured it's got to have personal preferences involved.
Besides, that option would need an extra val, and naming things is hardYes, and this is precisely why it is important to have these intermediate variables and visible names. Naming is hard because it forces the author to think more, so that the reader can think less. I'd rather do that work at writing time given that we write way less often than we read. Sometimes, it's not that hard, though ๐ But when it is, it's all the more important IMO. If it's super hard, sometimes it points out that we didn't break down into variables at the right moment, so it helps restructure.
David Kubecka
04/05/2023, 2:16 PMDavid Kubecka
04/05/2023, 2:17 PMval mergedBalances = mergeToBalanceHistory(runSumTransactions, aggregatedBalances)
But does it bring greater clarity? IMO notJoffrey
04/05/2023, 2:18 PM.run { }
Joffrey
04/05/2023, 2:19 PMJoffrey
04/05/2023, 2:20 PMDavid Kubecka
04/05/2023, 2:25 PMJoffrey
04/05/2023, 2:27 PMIt just depends on what you consider "big expressions" etcTrue, but maybe we can agree that if you had to wrap the expression over 2 lines, it can be considered "big"
Joffrey
04/05/2023, 2:32 PMAlso, it's partly what you are used to. If you are coming to Kotlin from plain Java you might be quite intimidated by scope functions in any context, thinking that it's just unnecessary display of "smartness".I totally agree. Though after coding in Kotlin for about 5 years, and using scope functions here and there, I still very often find variants without them to be more readable (unless the expression is extremely simple). Admittedly I'm big on extracting functions, so I rarely am in a situation where I need a scope function in the first place.
David Kubecka
04/05/2023, 2:38 PM