- Parsing a Url
- Fetching from the Url
- the URL may not be well formed, and
- fetching from a remote url may have network issues
So onto the basics of how such a call can be made using the Result type. You can imagine that parsing URL can return this Result type, capturing any exception that may result from such a call:
fun parseUrl(url: String): Result<URL> = kotlin.runCatching { URL(url) }
val urlResult: Result<URL> = parseUrl("http://someurl") urlResult.isSuccess == true urlResult.isFailure == false
urlResult.getOrNull() // Returns null if the block completed with an exception urlResult.getOrDefault(URL("http://somedefault")) // Returns a default if the block completed with an exception urlResult.getOrThrow() // Throws an exception if the block completed with an exception
val urlResult: Result<URL> = parseUrl("http://someurl") val hostResult: Result<String> = urlResult.map { url -> url.host }
val getResult: Result<String> = urlResult.mapCatching { url -> throw RuntimeException("something failed!") }
fun parseUrl(url: String): Result<URL> = kotlin.runCatching { URL(url) } fun getFromARemoteUrl(url: URL): Result<String> { return kotlin.runCatching { "a result" } }
val urlResult: Result<URL> = parseUrl("http://someurl") val getResult: Result<String> = urlResult.flatMap { url -> getFromARemoteUrl(url)}
I can do today is a bit of hack:
This concludes my exploration of the Result type and the ways to use it. I have found it to be a excellent type to have in my toolbelt.
val urlResult: Result<URL> = parseUrl("http://someurl") val getResult: Result<String> = urlResult.mapCatching { url -> getFromARemoteUrl(url).getOrThrow() }OR even better, create an extension function which makes flatMap available to "Result" type, this way and use it:
fun <T, R> Result<T>.flatMap(block: (T) -> (Result<R>)): Result<R> { return this.mapCatching { block(it).getOrThrow() } } val urlResult: Result<URL> = parseUrl("http://someurl") val getResult: Result<String> = urlResult.flatMap { url -> getFromARemoteUrl(url)}
What is the advantage of
ReplyDeleteval urlResult: Result = parseUrl("http://someurl")
val hostResult: Result = urlResult.map { url -> url.host }
over
try{
var host = parseUrl("http://someurl").host
} catch (e: SomeException) {
// handle it
}
With the first I have to deal with the error on every line. With the second I have one central place for error handling?
"With the first I have to deal with the error on every line." Yes and no. You do, in the sense that you can't do the plain old call you would have done, but that's a minor addition. And you can easily do a one-liner with Result as with an Exception, like you did:
Deleteval hostResult = parseUrl("http://someurl").map {it.host}
But the biggest reason for using Result is its explicit nature. You explicitly state within the return type that there might be an exception. And with all the helpful functions around Result, it typically ends up being less of a hassle than Java's checked exceptions. It's hard to explain, but it really is a bit nicer.
Good thing it's not difficult to add your own extension functions:
ReplyDeletefun Result.flatMap(func: (T) -> Result): Result =
this.fold(
onSuccess = func,
onFailure = Result::failure
)