DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Metal and the Simulated Annealing Algorithm
  • Reactive Kafka With Spring Boot
  • Why You Should Migrate Microservices From Java to Kotlin: Experience and Insights
  • How To Create a Homescreen Widget in Android

Trending

  • Failure Handling Mechanisms in Microservices and Their Importance
  • Rust and WebAssembly: Unlocking High-Performance Web Apps
  • Software Bill of Materials (SBOM): Enhancing Software Transparency and Security
  • AI Meets Vector Databases: Redefining Data Retrieval in the Age of Intelligence
  1. DZone
  2. Coding
  3. Languages
  4. Deriving a Kotlin ''Try'' Type

Deriving a Kotlin ''Try'' Type

Time to dive into a Try or two.

By 
Biju Kunjummen user avatar
Biju Kunjummen
·
Mar. 17, 21 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
3.7K Views

Join the DZone community and get the full member experience.

Join For Free

Functional programming languages like Scala often have a type called "Try" to hold the result of a computation if successful or to capture an exception on failure. 

This is an incredibly useful type, allowing a caller to pointedly control how to handle an exceptional scenario. In this post, I will try and create such a type from scratch.

As an example, I will be using the scenario from Daniel Westheide's excellent introduction to the Try type in Scala

So my objective is to call a remote URL and return the content as a string. A few things can go wrong:

  • The URL can be badly formed
  • The URL may be wrong and may not have anything to retrieve content from

Let's start with the first one, the URL being badly formed, an API call using the "Try" type would look something like this:

Kotlin
 




x


 
1
fun parseUrl(url: String): Try<URL> {
2
    return Try.of {
3
        URL(url)
4
    }
5
}



Here a URL is being parsed and the result is a valid URL or an exception. So a Try type that can implement this much, would look something like this:

Kotlin
 




xxxxxxxxxx
1
15


 
1
sealed class Try<out T> {
2
 
          
3
    class Success<T>(private val result: T) : Try<T>()
4
 
          
5
    class Failure<T>(private val throwable: Throwable) : Try<T>()
6
 
          
7
 
          
8
    companion object {
9
        fun <T> of(block: () -> T) = try {
10
            Success(block())
11
        } catch (e: Throwable) {
12
            Failure(e)
13
        }
14
    }
15
}



"Try" type has two sub types - A "Success" wrapping a successful result and a "Failure" capturing an exception from the call. 

With the two subtypes in place, let's extend the use of the Try type:

Kotlin
 




xxxxxxxxxx
1


 
1
val urlResult: Try<URL> = parseUrl("htt://somewrongurl")
2
assertThat(urlResult.isFailure()).isTrue()
3
assertThat(urlResult.isSuccess()).isFalse()



If I were to call using a badly formatted URL like above with a wrong scheme "htt" instead of "http", should result in a failure. So let's implement the "isFailure" and "isSuccess" behavior:

Kotlin
 




xxxxxxxxxx
1
15


 
1
sealed class Try<out T> {
2
 
          
3
    abstract fun isSuccess(): Boolean
4
    fun isFailure(): Boolean = !isSuccess()
5
 
          
6
    class Success<T>(private val result: T) : Try<T>() {
7
        override fun isSuccess(): Boolean = true
8
    }
9
 
          
10
    class Failure<T>(private val throwable: Throwable) : Try<T>() {
11
        override fun isSuccess(): Boolean = false
12
    }
13
 
          
14
    ...
15
}



That works nicely, so now that a URL is available, hopefully valid, let's get some content from the URL:

Kotlin
 




xxxxxxxxxx
1


 
1
val uriResult: Try<URL> = parseUrl("http://someurl")
2
val getResult: Try<String> = getFromARemoteUrl(uriResult.get())
3
assertThat(getResult.get()).isEqualTo("a result")



which means that our "Try" type should have a "get()" method to retrieve the result if successful and can be implemented like this:

Kotlin
 




xxxxxxxxxx
1
14


 
1
sealed class Try<out T> {
2
    ...
3
    abstract fun get(): T
4
 
          
5
    class Success<T>(private val result: T) : Try<T>() {
6
        ...
7
        override fun get(): T = result
8
    }
9
 
          
10
    class Failure<T>(private val throwable: Throwable) : Try<T>() {
11
        ...
12
        override fun get(): T = throw throwable
13
    }
14
}



The Success path simply returns the result and the Failure path propagates the wrapped exception.

map Operation

Let's take it a small step forward. Given a URL, say you want to return the host of the URL

Kotlin
 




xxxxxxxxxx
1


 
1
val uriResult: Try<URL> = parseUrl("http://myhost")
2
assertThat(uriResult.get().host).isEqualTo("myhost")



While this works, the problem with the approach is that the "get()" call for an invalid URL would result in an exception if the URL is not valid to start with, so a better approach is to retrieve the host name only if the URL is valid. Traditionally this is done using a "map" operator and a usage looks like this:

Kotlin
 




xxxxxxxxxx
1


 
1
val urlResult: Try<URL> = parseUrl("http://myhost")
2
val hostResult: Try<String> = urlResult.map { url -> url.host }
3
assertThat(hostResult).isEqualTo(Try.success("myhost"))



So let's add in a "map" operator to the "Try" type:

Kotlin
 




xxxxxxxxxx
1
22


 
1
sealed class Try<out T> {
2
    ...
3
    abstract fun <R> map(block: (T) -> R): Try<R>
4
 
          
5
    abstract fun get(): T
6
 
          
7
    data class Success<T>(private val result: T) : Try<T>() {
8
        ...
9
        override fun <R> map(block: (T) -> R): Try<R> {
10
            return of {
11
                block(result)
12
            }
13
        }
14
    }
15
 
          
16
    data class Failure<T>(private val throwable: Throwable) : Try<T>() {
17
        ...
18
        override fun <R> map(block: (T) -> R): Try<R> {
19
            return this as Failure<R>
20
        }
21
    }
22
}



and it behaves as expected.

flatMap Operation

Along the lines of "map" operation, now let's get back to the original scenario of validating the URL and then attempting to get the content. Now the call to get content can also fail, so you would want that to be wrapped with a Try type also. 

Kotlin
 




xxxxxxxxxx
1


 
1
val urlResult: Try<URL> = parseUrl("http://someurl")
2
val getResult: Try<String> = getFromARemoteUrl(urlResult.get())



The two calls need to be chained together, and "map" operation may appear to be the right operator to use: 

Kotlin
 




xxxxxxxxxx
1


 
1
val urlResult: Try<URL> = parseUrl("http://someurl")
2
val getResult: Try<Try<String>> = urlResult.map { url -> getFromARemoteUrl(url) }



If you look at the response type now, it does not really line up, it is a "Try<Try<String>>" and not a "Try<String>", this is exactly what a flatMap operation does. It takes a valid URL and returns just the inner wrapped result. A test using it would look like this:

Kotlin
 




xxxxxxxxxx
1


 
1
val urlResult: Try<URL> = parseUrl("http://someurl")
2
val getResult: Try<String> = urlResult.flatMap { url -> getFromARemoteUrl(url) }
3
assertThat(getResult).isEqualTo(Try.success("a result"))



So how can "flatMap" be implemented? With a fairly simple code that looks like this:

Kotlin
 




xxxxxxxxxx
1
22


 
1
sealed class Try<out T> {
2
    ...
3
    abstract fun <R> flatMap(tryBlock: (T) -> Try<R>): Try<R>
4
 
          
5
    data class Success<T>(private val result: T) : Try<T>() {
6
        ...
7
        override fun <R> flatMap(tryBlock: (T) -> Try<R>): Try<R> {
8
            return try {
9
                tryBlock(result)
10
            } catch (e: Throwable) {
11
                failure(e)
12
            }
13
        }
14
    }
15
 
          
16
    data class Failure<T>(private val throwable: Throwable) : Try<T>() {
17
        ...
18
        override fun <R> flatMap(tryBlock: (T) -> Try<R>): Try<R> {
19
            return this as Failure<R>
20
        }
21
    }
22
}



One more small feature, given that Try type has two subtypes is to deconstruct the contents when required:

Kotlin
 




xxxxxxxxxx
1
12


 
1
val urlResult: Try<URL> = parseUrl("http://someurl")
2
val getResult: Try<String> = urlResult.flatMap { url -> getFromARemoteUrl(url) }
3
when (getResult) {
4
    is Try.Success -> {
5
        val (s) = getResult
6
        println("Got a clean result: $s")
7
    }
8
    is Try.Failure -> {
9
        val (e) = getResult
10
        println("An exception: $e")
11
    }
12
}



This assumes that the user knows the subtypes which may be an okay assumption to make for this type. 

Conclusion

A type like "Try" is incredibly useful in capturing a result cleanly or with exception and provides a neat alternative to using a normal try..catch block. Here I showed a way to write such a type from scratch, however, this may be overkill, a better way to get such a type is to simply use an excellent library like vavr which has the Try type already built-in. I feel it is instructive to create such a type from scratch though.

Here is the code in my GitHub repository - https://github.com/bijukunjummen/fp-experiment-kotlin/blob/master/src/main/kotlin/sample/adt/Try.kt

Kotlin (programming language)

Published at DZone with permission of Biju Kunjummen, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Metal and the Simulated Annealing Algorithm
  • Reactive Kafka With Spring Boot
  • Why You Should Migrate Microservices From Java to Kotlin: Experience and Insights
  • How To Create a Homescreen Widget in Android

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!