I have a set of functions that have different signatures and parameters, but all of them could throw an exception. Instead of adding try-catch in each of them, I want to write an inline function that takes a function as an argument and calls this function within a try-catch block, and returns are certain types wrapping the exception.
// first function.
fun foo() -> String
// second function.
fun bar(argument: String) -> Boolean
// Result Type
sealed class Result<out R> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
}
// Wrapper Function
inline fun <T, R> safeCall(call: (T) -> R): Result<R>
// This Returns Result<String>
safeCall {
foo()
}
// This Returns Result<Boolean>
safeCall {
bar()
}
I am facing an issue while implementing the safeCall()
method. Ideally, I would want this to work for any underlying function. I can either make it to work for foo()
or bar(argument: String)
but not for both.
inline fun <T, R> safeCall(
call: (T) -> R, // This syntax forces to to have only one parameter.
): Result<R> {
return try {
val result: R = call() // This throws compilation error.
Result.Success(result)
} catch (e: Exception) {
Result.Error(e)
}
}
PS - Pretty new to Kotlin and Functional Programming.
CodePudding user response:
Sure! This sounds like a reasonable design pattern. Just a few notes.
inline fun <T, R> safeCall(call: (T) -> R): Result<R>
safeCall
's argument shouldn't itself need arguments. It's just a block of code that we're going to call, so this would be a more appropriate signature.
inline fun <R> safeCall(call: () -> R): Result<R>
Now as for how to actually implement it, it sounds like you have all of the right ideas. Just try to call the function inside of an actual Kotlin try
block.
inline fun <R> safeCall(call: () -> R): Result<R> {
try {
return Result.Success(call())
} catch (e: Exception) {
return Result.Error(e)
}
}
Then you can call it as
safeCall {
foo()
}
safeCall {
bar("Example Argument")
}
Remember that bar
takes an argument. Even if that argument is defined outside of the safeCall
scope, it still works through the magic of closures.
val x = "This works :)"
safeCall {
bar(x)
}
CodePudding user response:
By implementing your Result
class you really seem to duplicate a class from stdlib named... well, Result
:-) Your safeCall
is then named runCatching
:
// Result<String>
runCatching {
foo()
}
// Result<Boolean>
runCatching {
bar("Example Argument")
}