Result type for functional error handling in Kotlin - Time & Space Complexity
When using the Result type for error handling, it is important to understand how the time to process results grows as input changes.
We want to know how the cost of handling success or failure scales with the number of operations.
Analyze the time complexity of the following Kotlin code using Result type.
fun processList(items: List): Result> {
return items.map { item ->
if (item >= 0) Result.success(item * 2)
else Result.failure(Exception("Negative value"))
}.let { results ->
if (results.any { it.isFailure }) Result.failure(Exception("Contains failure"))
else Result.success(results.map { it.getOrThrow() })
}
}
This code processes a list of integers, doubling each if non-negative, and collects results using the Result type to handle errors.
Look for loops or repeated actions in the code.
- Primary operation: Mapping over the list twice (once to create Result objects, once to extract values).
- How many times: Each map runs once over all items, so twice over the list size.
As the list size grows, the number of operations grows proportionally.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 20 (2 passes x 10 items) |
| 100 | About 200 (2 passes x 100 items) |
| 1000 | About 2000 (2 passes x 1000 items) |
Pattern observation: The operations grow linearly with the input size, doubling due to two passes.
Time Complexity: O(n)
This means the time to process results grows in direct proportion to the number of items.
[X] Wrong: "Using Result type adds extra loops that make the code slower than linear."
[OK] Correct: Even with multiple passes, the total work still grows linearly with input size, so it remains efficient.
Understanding how error handling affects performance helps you write clear and efficient Kotlin code, a skill valued in many coding challenges and real projects.
"What if we combined the two map operations into one? How would the time complexity change?"
Practice
Result type represent in functional error handling?Solution
Step 1: Understand the purpose of Result type
The Result type is designed to wrap either a successful outcome or an error, making error handling explicit and functional.Step 2: Identify what Result holds
Result holds either a success value or a failure exception, not a list or a Boolean or an exception thrown directly.Final Answer:
An object that holds either a success value or a failure exception -> Option BQuick Check:
Result = success or failure [OK]
- Thinking Result is an exception type
- Confusing Result with Boolean return
- Assuming Result holds multiple errors
Result object in Kotlin?Solution
Step 1: Recall the Kotlin Result API
Kotlin providesResult.success(value)to create a successful Result wrapping the value.Step 2: Check method names
OnlyResult.successis the correct and existing method; others are invalid.Final Answer:
Result.success(value) -> Option DQuick Check:
Use Result.success() to wrap success [OK]
- Using non-existent methods like Result.ofSuccess
- Confusing with factory methods
- Trying to create Result with constructors
val result = runCatching { 10 / 0 }
result.onSuccess { println("Success: $it") }
result.onFailure { println("Failure: ${it.message}") }Solution
Step 1: Understand runCatching behavior
runCatching executes the block and catches exceptions, wrapping them in Result.failure.Step 2: Analyze division by zero
10 / 0 throws an ArithmeticException with message "/ by zero" which is caught and triggers onFailure.Final Answer:
Failure: / by zero -> Option AQuick Check:
Exception caught triggers onFailure [OK]
- Expecting program to crash with exception
- Thinking onSuccess runs despite error
- Ignoring exception message in output
Result for error handling:fun divide(a: Int, b: Int): Result<Int> {
return try {
Result.success(a / b)
} catch (e: Exception) {
Result.failure(e)
}
}
val result = divide(10, 0)
result.onSuccess { println("Result: $it") }
.onFailure { println("Error: ${it.message}") }Solution
Step 1: Check chaining onSuccess and onFailure
In Kotlin, onSuccess and onFailure are extension functions returning Unit, so chaining them directly causes a compile error.Step 2: Verify other parts
Try-catch with Result.success and Result.failure is correct; catching Exception is acceptable; Result.failure is valid.Final Answer:
Cannot chain onSuccess and onFailure directly on Result -> Option AQuick Check:
onSuccess and onFailure return Unit, no chaining [OK]
- Trying to chain onSuccess and onFailure
- Thinking catch block must catch ArithmeticException only
- Misusing Result.failure inside catch
Result wrapping the parsed integer from a string, or a failure if parsing fails. Which implementation correctly uses Result for this purpose?Solution
Step 1: Understand the goal
The function should return Result wrapping success if parsing works, or failure if it throws NumberFormatException.Step 2: Analyze each option
fun parseInt(str: String): Result<Int> = try { Result.success(str.toInt()) } catch (e: NumberFormatException) { Result.failure(e) } uses try-catch to wrap success or failure correctly. fun parseInt(str: String): Result<Int> = Result.success(str.toIntOrNull() ?: throw NumberFormatException()) throws exception outside Result.failure, breaking functional style. fun parseInt(str: String): Result<Int> = Result.failure(NumberFormatException()) always returns failure, ignoring input. fun parseInt(str: String): Int = str.toIntOrNull() ?: -1 returns Int, not Result.Final Answer:
fun parseInt(str: String): Result<Int> = try { Result.success(str.toInt()) } catch (e: NumberFormatException) { Result.failure(e) } -> Option CQuick Check:
Try-catch with Result.success/failure wraps parsing safely [OK]
- Throwing exceptions instead of wrapping in Result.failure
- Returning plain Int instead of Result<Int>
- Always returning failure ignoring input
Ready to prove it?
Pass the test and this pattern moves unknown
