How can I place `throw` inside helper-functions but still have null-safety? - kotlin

I want to wrap a throw in a helper-function, for logging purposes and such.
private fun chooseEmailAddress(user: UserProfile): EmailAddress {
val emailAddress = user.emailAddresses.find {
true // some business logic
}
if (emailAddress == null) {
throwAndNotice(CustomError(
message = "No Email Address found.",
))
}
return emailAddress
}
private fun throwAndNotice(err: CustomError) {
NewRelic.noticeError(err)
throw err
}
The problem:kotlin complains about a type-mismatch:
Type mismatch.
Required: Email
Found: Email?
I guess the compiler does not know that throwAndNotice always throws. If I inline the throwAndNotice method, it works, but it leads to duplication in about a dozen methods.
Is there a way I can tell the compiler "the following method always throws"? Or is there another idiomatic way to deal with this issue? I don't want to resort to !!.

Make it return Nothing. This indicates that it will never return (either throw an exception or infinite loop):
private fun throwAndNotice(err: CustomError): Nothing {
NewRelic.noticeError(err)
throw err
}
You can see other examples of doing this in the standard library, like TODO() and error().
Side note (as mentioned by dey in the comments):
The null check can be rewritten using ?: like this:
return emailAddress ?: throwAndNotice(...)

Related

how to clear the list after using

This is how I clear list after using, invalidDestination.clear(), but each time after I enter some invalid input, it will shows the previous error message.
for example
1st error message
"add fields abcd"
second time when I enter an invalid data like bcda, it should only return "add fields bcda", but the error message is, I already clear the list, what else should I do?
"add fields abcd, bcda"
private val validationErrors = mutableSetOf<ValidationError>()
private fun validateConfigTypeBduilder(configTypeBuilderList: List<ConfigTypeBuilder>, ruleAttributes: List<String>, destinationFieldList: List<String>) {
if (ruleAttributes.isNotEmpty()) {
var invalidDestination = mutableListOf<String>()
for (destinationField in destinationFieldList) {
if (!ruleAttributes.contains(destinationField)) {
invalidDestination.add(destinationField)
}
}
if (invalidDestination.firstOrNull { invalidDestination.contains(configTypeBuilder.destinationField) } != null)
addValidationError(""someMessage", "someMessage", $ADD_FIELDS $invalidDestination")
invalidDestination.clear()
}
}
private fun addValidationError(fieldPath: String, field: Any, error: String) {
logDataPathValidationError(fieldPath, field, error)
validationErrors.add(
ValidationError(
fieldPath,
error
)
)
}
internal fun logDataPathValidationError(dataPath: String, value: Any?, constraint: String) {
logger.info("{} {} value violates {} constraint", dataPath, value, constraint)
}
Thanks for Eric's inspire, I realized I should add clear destinationFieldList not invalidDestination after the if loop, to make sure next time calling validateConfigTypeBduilder with empty destinationFieldList
destinationFieldList.clear()
Each time you are calling validateConfigTypeBduilder a new list called invalidDestination is created. Then in the first loop you populate that list.
Then inside the if statement (the condition is a little weird the firstOrNull and the null check are irrelevant and removing them will produce the same result) you call addValidationError which logs the message and then clear the list.
If you are calling validateConfigTypeBduilder with the full list destinationFieldList then the number of errors reported will increase, since the list is produced and populated inside the function.
The list is getting cleared properly, but your code stops using it after clearing it.
Instead of sending the whole list addValidationError (assuming the invalidDestination list is ordered) you can send only the last element.
Changing this:
if (invalidDestination.firstOrNull { invalidDestination.contains(configTypeBuilder.destinationField) } != null)
addValidationError(""someMessage", "someMessage", $ADD_FIELDS $invalidDestination")
invalidDestination.clear()
}
to something like this:
invalidDestination.lastOrNull()?.let {
addValidationError(""someMessage", "someMessage", $ADD_FIELDS $it")
}
I don't fully understand you condition in the if statemente, so this is only an aproximation of the changes

nested let blocks in kotlin and valiadation

I want to handle validation in functional approach. I have user validation logic as below
if user object is null throw an exception
if the user is not active throw an exception
if the user type is super do nothing. If user type is admin do some operation.
In my code snippet I am throwing an exception if generated random number is even, otherwise complete the flow.
* You can edit, run, and share this code.
* play.kotlinlang.org
*/
import java.time.Instant
import java.util.UUID
import java.lang.IllegalStateException
import java.util.Random
fun main() {
val user : User? = User(UUID.randomUUID(),Instant.now(),UserStatus.ACTIVE,UserType.SUPER)
//val user : User? = User(UUID.randomUUID(),Instant.now(),UserStatus.ACTIVE,UserType.NORMAL) // works fine
user?.let{ existing -> existing.takeIf{it.status == UserStatus.ACTIVE}?.let{ activeUser ->
activeUser.takeUnless{ user -> user.userType == UserType.SUPER}?.let{
val number = Random().nextInt(5);
println(number)
if(number %2 == 0) throw IllegalStateException("invalid random number")
}
}?: throw IllegalStateException("User is not active right now")
} ?:throw IllegalStateException("user not created at all")
}
data class User(val uuid:UUID, val created:Instant, val status:UserStatus, val userType:UserType)
enum class UserStatus {
ACTIVE,INACTIVE
}
enum class UserType{
SUPER,NORMAL
}
when generated random number is odd its throwing an error message user is not active right now which is not correct. it should silently complete the function call.Any one help me what's wrong with the code?
In the inner block
existing.takeIf { it.status == UserStatus.ACTIVE }?.let { activeUser ->
activeUser.takeUnless { user -> user.userType == UserType.SUPER }?.let {
val number = Random().nextInt(5);
println(number)
if (number % 2 == 0) throw IllegalStateException("invalid random number")
}
} ?: throw IllegalStateException("User is not active right now")
You have a not null value.
Since you're not chaining the ?. calls. The better idea is to do early return/throw.
val existing = user ?: throw IllegalStateException("user not created at all")
So the nested block can be flattened.
The second exception "User is not active right now" happens when one of the two checks is not met.
The programming concept to describe this kind of business logic is not nullable variables. A regular old if check is much cleaner.
if (existing.status != UserStatus.ACTIVE || existing.userType == UserType.SUPER) {
throw IllegalStateException("User is not active right now")
}
BTW your error message seems wrong.
By "functional approach", I think you mean having the code expression-based. But nullable type is not powerful enough for your use case.
You should be looking for Either or Try. Both of them are available in the Arrow library. They are both monads. Their chaining (flatMap), like the ?.let calls, also require the callback hell.
In some languages, there is syntactic sugar to flatten the callback hell of flatMaps. You can take a look at the Monad Comprehensions, and see if you like that.
If not, no problem. In Kotlin, there is no shame associated with early return/throw.
Be pragmatic.

How to asynchronously map over sequence

I want to iterate over a sequence of objects and return the first non-null of an async call.
The point is to perform some kind of async operation that might fail, and I have a series of fallbacks that I want to try in order, one after the other (i.e. lazily / not in parallel).
I've tried to do something similar to what I'd do if it were a sync call:
// ccs: List<CurrencyConverter>
override suspend fun getExchangeRateAsync(from: String, to: String) =
ccs.asSequence()
.map { it.getExchangeRateAsync(from, to) }
.firstOrNull { it != null }
?: throw CurrencyConverterException()
IntelliJ complains:
Suspension functions can only be called within coroutine body
Edit: To clarify, this works as expected if mapping on a List, but I want to see how I'd do this on a sequence.
So I guess this is because the map lambda isn't suspended? But I'm not sure how to actually do that. I tried a bunch of different ways but none seemed to work. I couldn't find any examples.
If I re-write this in a more procedural style using a for loop with an async block, I can get it working:
override suspend fun getExchangeRateAsync(from: String, to: String) {
for (cc in ccs) {
var res: BigDecimal? = async {
cc.getExchangeRateAsync(from, to)
}.await()
if (res != null) {
return res
}
}
throw CurrencyConverterException()
}
You are getting an error, because Sequence is lazy by default and it's map isn't an inline function, so it's scope isn't defined
You can avoid using Sequence by creating a list of lazy coroutines
// ccs: List<CurrencyConverter>
suspend fun getExchangeRateAsync(from: String, to: String) =
ccs
.map { async(start = CoroutineStart.LAZY) { it.getExchangeRateAsync(from, to) } }
.firstOrNull { it.await() != null }
?.getCompleted() ?: throw Exception()
This doesn't give any errors and seems to be working. But I'm not sure it's an idiomatic way
I would suggest replacing Sequence with Flow. Flow api and behavior is pretty much same as for Sequence, but with suspending options.
https://kotlinlang.org/docs/reference/coroutines/flow.html
Code:
override suspend fun getExchangeRateAsync(from: String, to: String) =
ccs.asFlow()
.map { it.getExchangeRateAsync(from, to) }
.firstOrNull { it != null }
?: throw CurrencyConverterException()
FWIW, I found the suggestion in How to asynchronously map over sequence to be very intuitive. The code at https://github.com/Kotlin/kotlin-coroutines-examples/blob/master/examples/suspendingSequence/suspendingSequence.kt defines SuspendingIterator which allows next() to suspend, then builds SuspendingSequence on top of it. Unfortunately, you need to duplicate extension functions like flatMap(), filter(), etc. since SuspendingSequence can't be related to Sequence, but I did this and am much happier with the result than using a Channel.

Return from `buildSequence` in Kotlin

I'm using the buildSequence function in Kotlin. How do I end the iteration in the middle of the function? I'm looking for something similar to C#'s yield break statement.
My code looks something like the following. I'm stuck at the TODO.
fun foo(list:List<Number>): Sequence<Number> = buildSequence {
if (someCondition) {
// TODO: Bail out early with an empty sequence
// return doesn't seem to work....
}
list.forEach {
yield(someProcessing(it))
}
}
EDIT
Apparently, I misdiagnosed the source. The issue is not returning from the buildSequence function. The following works for me:
fun foo(list:List<Number>): Sequence<Number> = buildSequence {
return#buildSequence
list.forEach {
yield(someProcessing(it))
}
}
EDIT 2
The issue is that I put the return in a local helper function that validates data at multiple points in the buildSequence (Hence the helper function). Apparently I'm not able to return from buildSequence within the helper function. The error message was not terribly helpful...
Just use return#buildSequence, which is a labeled return from lambda, while an unlabeled return would mean 'return from the function foo'.
See also: Whats does “return#” mean?
Since Kotlin v 1.3.x preferred sequence syntax changed. (buildSequence is replaced by kotlin.sequences.sequence)
Updated "early return from generator" code snippet (includes try-catch and == null early return examples) for post 1.3.x Kotlin:
// gen# is just a subjective name i gave to the code block.
// could be `anything#` you want
// Use of named returns prevents "'return' is not allowed here" errors.
private fun getItems() = sequence<Item> gen# {
val cursor: Cursor?
try {
cursor = contentResolver.query(uri,*args)
} catch (e: SecurityException) {
Log.w(APP_NAME, "Permission is not granted.")
return#gen
}
if (cursor == null) {
Log.w(APP_NAME, "Query returned nothing.")
return#gen
}
// `.use` auto-closes Closeable. recommend.
// https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/use.html
cursor.use {
// iterate over cursor to step through the yielded records
while (cursor.moveToNext()) {
yield(Item.Factory.fromCursor(cursor))
}
}
}
(Thx for all the prior posts that helped me get on "named return" track.)

How to tell IDEA/Studio that the null check has been done?

I'm developing with Android Studio/IntelliJ IDEA.
I have enabled the inspection check called "Constant conditions & exceptions" that shows a warning if I am risking a NPE, such as:
String foo = foo.bar(); // Foo#bar() is #nullable
if (foo.contains("bar")) { // I'm living dangerously
...
}
I have the following in my code:
String encoding = contentEncoding == null ? null : contentEncoding.getValue();
if (!TextUtils.isEmpty(encoding) && encoding.equalsIgnoreCase("gzip")) {
inputStream = new GZIPInputStream(entity.getContent());
} else {
inputStream = entity.getContent();
}
Here's the source code of TextUtils#isEmpty(String):
/**
* Returns true if the string is null or 0-length.
* #param str the string to be examined
* #return true if str is null or zero length
*/
public static boolean isEmpty(CharSequence str) {
if (str == null || str.length() == 0)
return true;
else
return false;
}
I'm not risking any NPE because TextUtils#isEmpty(String) would return true to a null pointer.
However I'm still getting the little Method invocation 'encoding.equalsIgnoreCase("gzip")' may produce 'java.lang.NullPointerException' warning, which can be annoying.
Is it possible to make this check smarter and ignore the NPE warning if there's already a null-check done?
You can look into the link that Peter Gromov mention in his answer.
Created some simple classes that resemble your setup:
A class with a method annotated with #Nullable:
The TextUtil class with it's isEmpty method:
And finally the main class calling the TextUtil#isEmpty:
Now if you enter the File -> Settings... and go to Inspections ->Constant conditions & exceptions part you can change the Configure Assert/Check Methods to cater for your isEmpty method:
Add a new IsNull check method:
Enter the TextUtil class, isEmpty method and CharSequence parameter:
This gives this Assert/Check Method Configuration window:
Press Ok and then Ok again to go back to the editor view and you'll see that the inspection disappeared:
You are actually telling IntelliJ that the isEmpty method is doing a null check on the str parameter.
You could use //noinspection ConstantConditions that will remove the NPE warning for the following line, like this:
String encoding = contentEncoding == null ? null : contentEncoding.getValue();
//noinspection ConstantConditions
if (!TextUtils.isEmpty(encoding) && encoding.equalsIgnoreCase("gzip")) {
inputStream = new GZIPInputStream(entity.getContent());
} else {
inputStream = entity.getContent();
}
You can use #SuppressWarnings("ConstantConditions") annotation.
#SuppressWarnings("ConstantConditions")
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int indexViewType) {
if (inflater == null) {
inflater = LayoutInflater.from(parent.getContext());
}
ItemViewProvider provider = getProviderByIndex(indexViewType);
provider.adapter = MultiTypeAdapter.this;
return provider.onCreateViewHolder(inflater, parent);
}
Select "TextUtils.isEmpty".
Right Click -> Show Context Actions -> Add Method Contract.
Enter "null -> true".
Save the configuration xml.
Please check the details here
See http://www.jetbrains.com/idea/webhelp/configuring-check-assert-methods.html for IDEA 12.
In IDEA 13 EAP, you can add method contract: http://youtrack.jetbrains.com/issue/IDEA-93372
Unfortunately marked as "right answer" solution is of date. But I found equivalent for me solution.
The new versions of IDE work correctly with static methods. So the example from the question won't throw warning anymore.
TextUtils#isEmpty(String);
public static boolean isEmpty(CharSequence str) {
// your checks
}