Option Chaining instead of if/else - kotlin

Is there a more succint way to write the following code using option chaining and/or the elvis operator?
email.addSubject(if (creator != null) String.format( inviteDescription, creator) else String.format(inviteDescriptionNoCreator, group))
It feels like there should be.

Using the normal IF expression
val subject = if (creator != null) {
inviteDescription.format(creator)
} else {
inviteDescriptionNoCreator.format(group)
}
email.addSubject(subject)
The Elvis Operator
val subject = creator?.let {
inviteDescription.format(it)
} ?: inviteDescriptionNoCreator.format(group)
email.addSubject(subject)
If the goal is to write the shortest code possible then you could go with a single line Elvis operator. But if the goal is a readable code, I would choose the simple if expression or a multi line Elvis operator. I would even go one step ahead and move it to a separate method. But whatever you choose, please don't write everything in a single long line, rather put it in separate lines.

Just taking advantage of ?. and ?: gives us the following:
email.addSubject(creator?.let { String.format(inviteDescription, it) } ?: String.format(inviteDescriptionNoCreator, group))
Unfortunately, that's still quite long and is arguably not much easier to read. You could shave off a bit more by using the String.format extension function:
email.addSubject(creator?.let { inviteDescription.format(it) } ?: inviteDescriptionNoCreator.format(group))

Related

Kotlin: loop through finite values returned by a next function

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.

refactor if to takeIf and return "return" without label kotlin

I am trying to write more idiomatic Kotlin code and I am stuck with the best way to refactor this if condition. Basically when the condition if true (fragment is GenericActionsBottomSheetDialog instance in a list of Fragments) I return the funcion itself.
Here is what I had and how I refactored it. Is there better way to achieve it? After my refactoring it get worse:
Before refactor:
supportFragmentManager.fragments.iterator().forEach {
if (it is GenericActionsBottomSheetDialog)
return
After refactor:
supportFragmentManager.fragments.iterator().forEach { it ->
it.apply {
takeIf { it is GenericActionsBottomSheetDialog }?.apply { return }}}
If this forEach is the only thing in your current function (which it should IMO), you could get rid of the non-local return by using takeWhile instead:
supportFragmentManager.fragments
.takeWhile { it !is GenericActionsBottomSheetDialog }
.forEach {
// do stuff
}
/!\ be careful that this changes semantics if there is other stuff after the forEach in the same function declared with fun.
If you expect many fragments in the list, you could also use asSequence() before takeWhile so you don't create an intermediate list.
Here's one possibility, which separates the decision from the action:
if (supportFragmentManager.fragments.any{ it is GenericActionsBottomSheetDialog })
return
I think this approach makes the intent clearest. (It's also about the most efficient.)
any() simply checks each item in turn, stopping when it finds a match (or when it reaches the end of the list). Kotlin has many functions like this (inspired by functional programming languages) that use lambdas to operate on lists and other structures. They tend to be named for what they do, rather than how they do it — which makes code using them both short and easy to read. (You should be writing code for people to read, as much as for computers to execute!)
For completeness, here's another approach, which uses filterIsInstance():
if (supportFragmentManager.fragments
.filterIsInstance<GenericActionsBottomSheetDialog>)
.isNotEmpty())
return
There are bound to be many other ways. But I agree with the commenter that your ‘refactored’ approach, while using many more Kotlin functions, has little else to recommend it!
This is an opinion based question, and answers cannot be any different.
That being said: there is nothing wrong with if clauses. From what I can see from your current question, I'd leave it with an if.
Now, if you really do not want to use it, filter elements that are not of type GenericActionsBottomSheetDialog and apply whatever function you want on them (the part that is in your else clause, which we do not see).
EDIT:
In case you only want to check if the object of the GenericActionsBottomSheetDialog exists in the collection, you can perhaps do it like this:
val dialogExists = supportFragmentManager.fragments
.firstOrNull { it is GenericActionsBottomSheetDialog} != null
if (dialogExists) {
return
}
#gidds solution is IMO the most idiomatic one:
if (supportFragmentManager.fragments
.any { it is GenericActionsBottomSheetDialog }) return
I would like to add this solution eliminating the if:
supportFragmentManager.fragments
.firstOrNull { it is GenericActionsBottomSheetDialog }
?.run { return }
It's a matter of taste which one you pick, I prefer the first one.
I was wondering why you use the iterator? You could simply do:
supportFragmentManager.fragments.forEach {

Elvis operator with Run in kotlin

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 {
//...
}

Is it considered bad convention when in iterating through two maps, I don't check if key exists in one of them?

I have two maps, let's call them oneMap and twoMap.
I am iterating through all the keys in oneMap, and if the key exists in twoMap I do something
like
fun exampleFunc(oneMap: Map<String, Any>, twoMap: Map<String, Any>) {
for((oneMapKey, oneMapVal) in oneMap) {
if (twoMap.containsKey(oneMapKey)) {
val twoMapVal = twoMap[oneMapKey]
if (twoMapVal == oneMapVal) {
//do more stuff
}
//do more stuff, I have more if statements
}
}
}
To avoid having more nested if statements, I was wondering if instead I could get rid of the
if (twoMap.containsKey(oneMapKey)) check. if twoMap doesn't contain the oneMapKey, we get a null object, and my code still works fine. I was wondering if this is considered bad convention though
fun exampleFunc(oneMap: Map<String, Any>, twoMap: Map<String, Any>) {
for((oneMapKey, oneMapVal) in oneMap) {
val twoMapVal = twoMap[oneMapKey]
if (twoMapVal == oneMapVal) {
//do more stuff
}
//do more stuff, I have more if statements
}
}
It depends. Do you wanna execute the "more stuff" or not?
If you do not wanna execute it you should keep the if condition. Though, if you are concerned about indentation (and deep if hierarchies) you can consider breaking out of the loop:
for((oneMapKey, oneMapVal) in oneMap) {
if (!twoMap.contains(oneMapKey)) continue // continue with next iteration
// do more stuff
}
If your map does not contain null values you can also get the value and check if the result was null (which means the key was not present in the map):
for((oneMapKey, oneMapVal) in oneMap) {
val twoMapVal: Any = twoMap[oneMapKey] ?: continue // continue with next iteration
// do more stuff
}
So its always good practice the remove useless code and (in my opinion) to have less if-hierarchies, as you can easily loose focus when you have lots of nested conditions.
As Tenfour04 says, omitting the containsKey() check is only an option if the map values aren't nullable; if they are, then []/get() gives no way to distinguish between a missing mapping and a mapping to a null value.
But if not (or if you want to ignore null values anyway), then I'd certainly consider omitting the check; the resulting code would be slightly shorter and slightly more efficient, without losing clarity or maintainability.  It could also avoid a potential race condition.  (Though in a multi-threaded situation, I'd be considering more robust protection!)
One variation is to use let() along with the safe-call ?. operator to restrict it to non-null cases:
for ((oneMapKey, oneMapVal) in oneMap) {
twoMap[oneMapKey]?.let { twoMapVal ->
if (twoMapVal == oneMapVal) {
// Do more stuff
}
// Do more stuff
}
}
Using ?.let() this way seems to be a fairly common idiom in Kotlin, so it should be fairly transparent.

Pass no value to function with default parameters

I have this Kotlin function:
fun doSomething(user: User = defaultUser) {
//do something
}
and I call it from another place:
val user: User? = getUser()
if (user == null) {
doSomething()
} else {
doSomething(user)
}
Is it possible to improve this code? I think this "if/else" is a little bit messy. Is possible to do something like this?
doSomething(user ?: NoValue)
You can cut it down to user?.run(::doSomething) ?: doSomething() (if doSomething doesn't return null) but I don't know why you'd want to!
Honestly the if/else reads nice to me, stick it on one line without the braces and it's nice and compact. Unfortunately I don't think you can conditionally add parameters into a function call (and handling default parameters can get unnwieldy when you have a few).
I agree with #benjiii, it might be better to have a nullable parameter and handle the default internally, if you don't need to use null as a legit value
You could do something like this:
getUser()?.let { // user is not null
doSomething(it)
} ?: run { // user is null here
doSomething()
}
(cf: Swift 'if let' statement equivalent in Kotlin)
I don't think you could do something shorter without making the code hard to understand Edit 2: Actually you can, see the comment
Edit: I would personally handle the nullable variable inside the function like this:
fun doSomething(user: User?) {
val correctUser = user ?: defaultUser
//do something
}
so you can use the function like this:
doSomething(getUser())
I agree with cactustictacs, just putting it on one line is clear and simple. However, if you use it often and it's bothering you, it's easy enough to wrap it in a function without the default parameter:
fun doSomethingSensibly(user: User?) =
if (user == null)
doSomething()
else
doSomething(user)
Which can be used as:
doSomethingSensibly(getUser())