I have a Kotlin application that filters a collection elements in a manner similar to the following:
fun main() {
val strings = arrayOf("A", "B", "C")
val acceptAll = true
println(
strings.filter {
if (acceptAll) {
true // "The expression is unused"
}
// Other filters
false
}
)
}
However, the true line is highlighted by IntelliJ with the message "The expression is unused". Sure enough, my program does not hit that code path, the filter returns false, and I end up with an empty array.
This is a simplified example, but it illustrates what it is that I'm trying to accomplish.
Any suggestions would be greatly appreciated.
Thank you!
To actually return the value true from the lambda inside that if statement, you have to replace true with return#filter true.
The statement true does not mean "return true", it only means that when evaluated, the result of the evaluation will be true. Only the last statement of a lambda is considered the return value. And since true was not the last statement, it was not returned. After evaluating true, execution jumped out of the if-case body and executed the next statement, which is false. Since that is also the last statement in the lambda, it was also the return value of the lambda, even though it executed the body of the if statement just before that.
You can leave the statement false without changes. Since it is the last statement (contrary to true) in the lambda, it will also be the return value from the lambda if that statement is executed. For symmetry, I would personally change it to return#filter false, however.
The docs on this can be found at Returning a value from a lambda expression and Return at Labels
Related
Background info
A common pattern in some programming languages is to have a function that when called, returns the next value until the end of the finite sequence is reached, in which case it keeps returning null.
A common example in Java is this:
void printAll(BufferedReader reader) {
String line;
// Assigns readLine value to line, and then check if not null
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
It is similar to the iterator in the Iterator design pattern, but the iterator has a next(): Object and a hasNext(): Boolean, whereas the BufferedReader has no hasNext() check functionality, but only the form next(): Object?, in which the returned object can be null to mark the end of the sequence. I call functions such as next() a "next function" (or maybe "yield" function), but I don't know if there is an word for this pattern.
In Java, an expression can contain assignments, which allows constructs such as: (line = reader.readLine()) != null. This code assigns the nullable value of readLine() to line, and then check whether the value in line is not null. But Kotlin doesn't allow such constructs, because in Kotlin, an assignment is not an expression, so it cannot be used as loop condition in Kotlin.
Question
What are the possible patterns in Kotlin to loop through the finite number of values returned by a next function, such as readLine()?
(Next functions can also be found for example in ZipInputStream, to go to the next zip entry.)
I'm not simply looking for a Kotlin workaround for this problem, because I can program that myself without problems. I'm looking to explore the possible patterns so that people can select one that suits their needs.
I have found some patterns myself, which I'll post here as an answer, but there may be more patterns out there, which would be interesting to know.
I've ordered to solutions by (what I believe) the best solution in descending order.
Solution 1: using built-in generateSequence (recommended)
I just found out that Kotlin has a built-in standalone generateSequence() function (located in the kotlin.sequences package).
generateSequence { br.readLine() }
.forEach { line ->
println("Line: $line")
}
generateSequence accepts a code block that you can provide, that must generates a value. In this case, br.readLine() is the code block, and generates either a String, or null if the end is reached. generateSequence generates a sequence that internally calls readLine() when the next value is requested from the sequence, until readLine() return null, which terminates the sequence. So sequences in Kotlin are lazy: they don't read neither know all the values ahead of time, only a single readLine() is called when for example forEach processes a single line. This laziness is usually exactly what you want, because it saves memory and minimizes an initial delay. To change it to eagerly, you can append generateSequence { br.readLine() } with .toList().
Pros 1: no additional variables.
Pros 2: just one construct (generateSequence).
Pros 3: returns a Sequence, so you can chain additional methods such as filter().
Pros 4: any sign of nullability is abstracted away. (No null keywords, nor ? nor ! operators.)
Pros 5: adheres a functional programming style.
IMO, this is the cleanest solution that I've seen so far.
Solution 2: while true loop with elvis break call
while (true) {
val line = br.readLine() ?: break
println("Line: $line")
}
Pros: no additional variables.
Cons: some people don't like while-true loops and break statements.
Solution 3: do-while with safe call also
do {
val line = br.readLine()?.also { line ->
println("Line: $line")
}
} while (line != null)
Pros: no additional variables.
Cons: less readable than other solutions.
Solution 4: next before start and at end of each iteration
This is probably the most common solution for Java programmers who are new to Kotlin.
var line = br.readLine()
while (line != null) {
println("Line: $line")
line = br.readLine()
}
Cons 1: duplicated next (readLine) call and a duplicated assignment.
Cons 2: reassignable var.
Solution 5: while loop with assignment using also
This is the solution generated by IntelliJ when converting Java to Kotlin code:
var line: String?
while (br.readLine().also { line = it } != null) {
println("Line: $line")
}
Cons: line is declared as nullable, even though it can never be null inside the loop. So you'll often have to use the not-null assertion operator if you want to access members of line, which you can limit to one assertion using:
var nullableLine: String?
while (br.readLine().also { nullableLine = it } != null) {
val line = nullableLine!!
println("Line: $line")
}
Cons 1: requires not-null assertion even though it can never be null inside the loop.
Cons 2: reassignable var.
Cons 3: less readable than other solutions.
Note that if you change var line: String? to var line: String, the code still compiles, but it will throw a NPE when line becomes null, even though there are no not-null assertions used.
Im having a bit of trouble with this situation
a?.let {
b?.let { }
}?: run { }
The thing is, if "b" is null, the run block is executed, even though the elvis operator is referencing the "a" let.
I already tried to use "apply" instead of "run", same thing happens
The reason is that the let function returns whatever its last expression is. If the last expression evaluates to null (as b?.let would if b is null or the last line of that inner let evaluates to null), then the second part of the Elvis operator will be evaluated.
The solution is to never follow up a scope function call with an Elvis operator. It would work OK with also instead of let since it doesn't return the lambda result, but it's still obtuse-looking code that's hard to read. It's such an ugly pattern to use that people make memes about how ridiculous it is.
For this particular case, I would refactor your code as follows.
val a = a
if (a != null) {
b?.let {
//...
}
} else {
//...
}
I was reading some source code on coroutines and run into this function;
private fun cancelParent(cause: Throwable): Boolean {
// CancellationException is considered "normal" and parent is not cancelled when child produces it.
// This allow parent to cancel its children (normally) without being cancelled itself, unless
// child crashes and produce some other exception during its completion.
if (cause is CancellationException) return true
if (!cancelsParent) return false
return parentHandle?.childCancelled(cause) == true
}
The point that I don't quite get is the very first line of code. It feels like it contradicts with what's stated in the comment. If the exception is CancellationException then it's a "normal" cancellation and the parent should not be cancelled, right? However, the function returns true which is read like - "Ok, I'm gonna cancel the parent".
By the way, the rest of the lines/checks in the function make sense to me when I look into what, for example supervisorScope or launch, returns.
Can someone please explain?
That's one of the cases where naming return values would be valuable.
If you look at the usage of this code, you'll see the following:
// Now handle the final exception
if (finalException != null) {
val handled = cancelParent(finalException) || handleJobException(finalException)
if (handled) (finalState as CompletedExceptionally).makeHandled()
}
So, true means not shouldParentBeCancelled?, as one may assume, but wasCancellationAlreadyHandledOrShouldBeHandledByParent?
I wish to modify a mapping value isUsed in isValid if-else statement and return a boolean value based if else
I tried including the view keyword in the function declaration but it resulted in unaltered results
function createVoucher(string val )public
{ var stud= VKey[val];
isKey[val]=true;
stud.key=val;
stud.count=1 }
function isValid(string val)public view returns(bool){
if(isKey[val] && isUsed[val]==false)
{var stud=VKey[val];
isKey[val]=false;
stud.count=stud.count--;
isUsed[val]=true;
return true;}
else return false }}
`
I expected a created voucher would return true on first call of isValid() and false on subsequent calls
isValid is declared as a view function, so when you call it, the code is just executed locally on one node and no transaction is added to the blockchain. This means it can't mutate any state.
You'll need to remove the view modifier if you want your function to change storage values. But note that transactions do not have return values, so you'll no longer be able to get the boolean value out. You may instead want to emit an event or use a separate function to check isUsed.
Consider the following code example:
TempList.ForEach(Function(obj)
obj.Deleted = True
End Function)
And this one:
TempList.ForEach(Function(obj) obj.Deleted = True)
I would expect the results to be the same, however the second code example does NOT change the objects in the list TempList.
This post is more to understand why...? Or at least get some help understanding why...
It's because you used Function instead of Sub. Since a Function returns a value, the compiler considers that the equals sign (=) is used as a comparison, not an assignment. If you change Function to Sub, the compiler would correctly consider the equals sign as an assignment:
TempList.ForEach(Sub(obj) obj.Deleted = True)
If you had a multiline lambda; you wouldn't have had this problem:
TempList.ForEach(Function(obj)
obj.Deleted = True
Return True
End Function)
Obviously, for the ForEach method it makes no sense to use a Function because the return value wouldn't be used, so you should use a Sub.