For example, in Java switch case, there is a difference between having break and not having break
However, Kotlin’s when lacks this function and cannot achieve the effect of example code in Java
// Example code
int i = 0;
switch(i){
case 0:
System.out.println("0");
case 1:
System.out.println("1");
break;
case 2:
System.out.println("2");
// The result is shown as:
// 0
// 1
I am rewriting a Go program with Kotlin.
So now I have to use a lot of if to solve the problem, because it has a lot of case parts, and use penetration function
I know the feeling of porting code from one language to another. There are often language features that look so similar but have a small difference that makes porting code really annoying. That said I don’t mind the if statements in this example. Maybe it depends on the number of them but if it’s to many to comfortably fit on one screen maybe you should think about other ways to simplify that algorithm.
(By the way, this behaviour is normally called ‘fallthrough’; that word would make this thread a bit easier to follow.⠀ ‘Penetration’ has… other associations in English.)
Maybe it would be good to focus on use cases that don’t all break (already covered by when) or all fallthrough (better covered by if-statements IMO)
I suspect allowing the classic style of break/fallthrough will fall for the common trap of obfuscating the jumps between cases like it does in many other languages. Personally, I prefer to refactor to allow some nesting to organize jumps instead of requiring others to do my mental math and play through each case in their head as they watch for breaks or fallthroughs.
Maybe there’s another syntax change that could better solve the issue (if it’s a big enough issue) that doesn’t hide jumps the way break/fallthrough does?
Here’s the steps all collected into runnable examples (looks like runnable examples don’t work when hidden inside a details block):
Imperative
fun main() {
val scoringRules = mapOf(98 to 5200, 97 to 1500, 95 to 100)
var score = 0
val level = 97
for ((ruleLevel, multiplier) in scoringRules) {
score += if (level >= ruleLevel) (level - ruleLevel) * multiplier else 0
}
println(score)
}
Functional
fun main() {
val scoringRules = mapOf(98 to 5200, 97 to 1500, 95 to 100)
var score = 0
val level = 97
// You could use fold, reduce, or other operations here. I chose sumBy since it's easier for those not familier with function coding and we don't care about doing anything fancy with the accumulator
score += scoringRules.entries.sumBy { (ruleLevel, multiplier) ->
if (level >= ruleLevel) (level - ruleLevel) * multiplier else 0
}
println(score)
}
Functional and refactored into an extension function
fun main() {
val scoringRules = mapOf(98 to 5200, 97 to 1500, 95 to 100)
var score = 0
val level = 97
score += scoringRules.calculateScore(level)
println(score)
}
fun Map<Int, Int>.calculateScore(level: Int) = entries.sumBy { (ruleLevel, multiplier) ->
if (level >= ruleLevel) (level - ruleLevel) * multiplier else 0
}
IMO the functional aproach should use filter befor summing the results. It’s easier to read that way because you can get ride of the if-else expression. So it should be
But I accept that this is my personal preference and both solutions are equally valid. Also based on the optimizations done by the JVM (or other target platform) your solution might be a bit faster because filter creates a new list of items but I think most modern JVMs might be able to optimize this away because the list is only used once directly after creating it.