avatarGabriel Shanahan

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

4437

Abstract

however this is not rich enough for us. Nullables/optionals are fine for representing success, but if something goes wrong, we probably want to encode some information about what went wrong - the '<i>oops instance</i>', so to speak. And nullables/optionals only have the ability to communicate "something" vs "nothing". What we want is <code>Success(value: T)</code> vs <code>Failure(oops: Throwable)</code>.</p><p id="ccb5">So…let’s do exactly that! Let’s create a type <code>Result<out T></code> and two subclasses <code>Success<T>(val value: T): Result<T></code> and <code>Failure(val error: Throwable) : Result<Nothing></code> and use those to wrap all of our calculations.</p> <figure id="87e0"> <div> <div> <img class="ratio" src="http://placehold.it/16x9"> <iframe class="" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fpl.kotl.in%2Fus1wVviSO&amp;display_name=Kotlin+Playground&amp;url=https%3A%2F%2Fpl.kotl.in%2Fus1wVviSO&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=kotl" allowfullscreen="" frameborder="0" height="300" width="800"> </div> </div> </figure></iframe></div></div></figure><p id="64cd">Hmmm…could we do better? Yes we could:</p> <figure id="d53d"> <div> <div> <img class="ratio" src="http://placehold.it/16x9"> <iframe class="" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fpl.kotl.in%2FfYB0JFFqQ&amp;display_name=Kotlin+Playground&amp;url=https%3A%2F%2Fpl.kotl.in%2FfYB0JFFqQ&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=kotl" allowfullscreen="" frameborder="0" height="300" width="800"> </div> </div> </figure></iframe></div></div></figure><p id="00f2">By introducing two simple types and a function, we were able to:</p><ul><li>recover normal execution flow</li><li>get rid of spooky “action-at-a-distance” exception handlers</li><li>write code that is shorter, cleaner, and almost as simple as code where we completely ignore exceptions</li><li>deal with <b>all possible errors</b>, now and in the future</li><li>be explicit that this operation can fail, and use types to force calling code to deal with these failures (incidentally, this is what checked exceptions were trying, and <a href="https://news.ycombinator.com/item?id=827540">mostly</a> <a href="http://twistedoakstudios.com/blog/Post399_improving-checked-exceptions">failing</a>, <a href="https://www.ckwop.me.uk/Why-Exceptions-Suck.html">to do</a>)</li></ul><p id="c8e0">However, most importantly, we have shifted our mindset. We no longer view exceptions as control flow constructs, we view them as simple carriers of information — basically data classes. We don’t immediately feel the need to translate or chain them, unless there is an actual need to provide more information or react to them — for the most part, the type won’t matter.</p><p id="69f2">The benefit of this approach gets multiplied when you start dealing with libraries that represent errors in different ways.</p><p id="e725">Here’s an example:</p> <figure id="d6d9"> <div> <div> <img class="ratio" src="http://placehold.it/16x9"> <iframe class="" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fpl.kotl.in%2Frr7HKMygP&amp;display_name=Kotlin+Playground&amp;url=https%3A%2F%2Fpl.kotl.in%2Frr7HKMygP&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=kotl" allowfullscreen="" frameborder="0" height="300" width="800"> </div> </div> </figure></iframe></div></div></figure><p id="c245">Given those functions, we are tasked with creating <code>ratioOfLibFuns()</code> which returns the <code>Int</code> ratio of both library functions, and deals with errors appropriately. In this specific scenario, if the <code>Int</code> returned by <code>libFun2</code> is missing, it means that the user forgot to do something, and we want to convey this information somehow.</p><p id="40e2">Here’s how we would probably implement this up until now:</p> <figure id="c55c"> <div> <div> <img class="ratio" src="http://placehold.it/16x9"> <iframe class="" src="https://cd

Options

n.embedly.com/widgets/media.html?src=https%3A%2F%2Fpl.kotl.in%2Fuaox6kG6x&display_name=Kotlin+Playground&url=https%3A%2F%2Fpl.kotl.in%2Fuaox6kG6x&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=kotl" allowfullscreen="" frameborder="0" height="300" width="800"> </div> </div> </figure></iframe></div></div></figure><p id="bebe">Ugh, what a mess. Thankfully, using what we just discovered, we can do better:</p> <figure id="450c"> <div> <div> <img class="ratio" src="http://placehold.it/16x9"> <iframe class="" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fpl.kotl.in%2FsdsN6dBgC&amp;display_name=Kotlin+Playground&amp;url=https%3A%2F%2Fpl.kotl.in%2FsdsN6dBgC&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=kotl" allowfullscreen="" frameborder="0" height="300" width="800"> </div> </div> </figure></iframe></div></div></figure><p id="7995">By introducing a super simple type, we managed to make to code about 4x shorter, increase its readability and maintainability, and become explicit about the fact that it can fail.</p><p id="925f">In fact, we could go further — we could combine this approach with what we learned about <a href="https://readmedium.com/sealed-hierarchies-strongly-typed-illegal-states-3d3c50c9a77b">strongly typed illegal states</a>, and avoid using <code>UserRatioException</code> completely:</p> <figure id="6941"> <div> <div> <img class="ratio" src="http://placehold.it/16x9"> <iframe class="" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fpl.kotl.in%2Fsiyxi50Pg&amp;display_name=Kotlin+Playground&amp;url=https%3A%2F%2Fpl.kotl.in%2Fsiyxi50Pg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=kotl" allowfullscreen="" frameborder="0" height="300" width="800"> </div> </div> </figure></iframe></div></div></figure><p id="15a2">Notice how much information you gain just by reading the signature of the function: it returns a <code>Ratio</code> instance, which recognizes a single type of business error (<code>Int2Missing</code>), and is wrapped in <code>Result</code>, which signifies that the method can fail unexpectedly. Without even looking at the implementation, you immediately know about all the things that can possibly happen in this method.</p><p id="c9b6">This is one of the incredibly powerful consequences of using types which have implicit <i>meaning</i>/<i>behavior</i> associated with them. You actually know this very well, but might not realize it. Think about it — <code>Optional</code> means a presence or absence of a value, <code>List</code> means multiple values, <code>Future</code> is a value which will exist at some point in time, <code>Function</code> is a value that can be<i> produced</i>, <code>Result</code> is a value or a failure, and so on.</p><p id="8f77">This is actually one of the core principles of <i>actual</i> functional programming — using these datatypes, as well as many other ones such as <code>Either</code>, <code>Writer</code>, <code>State</code>, <code>Eval</code> and many, many others, to represent <i>behavior</i> in a program, and building what we need as a composition of these fundamental, provably correct behaviors. This topic is far beyond the scope of this article, but I thought it was worth mentioning that there was a close connection with what we’re doing here with <code>Result</code>.</p><p id="20a7">It turns out that the <code>Result</code> hierarchy and the <code>runCatching</code> method are already part of the standard library. In the next article, we’ll explore what’s included in the standard library more closely.</p><p id="3c25">Go back to <a href="https://readmedium.com/programming-with-result-motivation-dec875787a7">Programming with Result: Motivation</a>, jump to the <a href="https://readmedium.com/table-of-contents-c52573cfa291">Table of Contents</a>, or continue to <a href="https://readmedium.com/programming-with-result-kotlin-result-836972cfb29e">Programming with Result: <code>kotlin.Res</code>ult</a>.</p><figure id="8ecd"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*biBSB579iezsNvEQ_NMLBg.png"><figcaption></figcaption></figure></article></body>

Programming with Result: Returning a Result

How to solve all the problems of exceptions by instead returning values. An introduction to the Result class, and how it makes handling unexpected errors a breeze. A quick note about how it relates to actual functional programming.

— — — — — — — — — — — — — — —

THE CURRENT VERSION OF THIS ARTICLE IS PUBLISHED HERE.

— — — — — — — — — — — — — — —

Tags: #FUNDAMENTAL CONCEPT

This article is part of the Kotlin Primer, an opinionated guide to the Kotlin language, which is indented to help facilitate Kotlin adoption inside Java-centric organizations. It was originally written as an organizational learning resource for Etnetera a.s. and I would like to express my sincere gratitude for their support.

It is recommended to read the Introduction before moving on. Check out the Table of Contents for all articles.

In the previous article, we showed how exceptions were basically GOTO in disguise, broke fundamental OOP principles and resulted in code that was prone to incorrect error handling, difficult to reason about and time-consuming to write correctly.

Here are the specific problems we want to solve:

  1. We want to preserve normal control flow. This means that we don’t want execution to jump a thousand light-years away from the place the error happens.
  2. We want to deal with all error states, not just the “typical” ones
  3. We want to decouple our code from the specific exceptions thrown by outside code as much as possible
  4. We want to write less code

While this may seem like a tall order, it’s actually not.

Here are some observations:

  1. Normal control flow is preserved when we exit the function by returning a value. The moment we stop throwing exceptions, we have no other way of exiting a function, so when an exception gets thrown, we need to stop re-throwing it as quickly as possible. Since exceptions are, almost without exception (ha), eventually converted to values, what we’re really saying is that we need this conversion to happen as close to the place the error happened as possible — i.e. we need to move the code that does the conversion to values as deep as we can.
  2. In 99% of the cases, all you do is transform the Throwable into a string representation that gets sent to the client (and maybe written to the log). You don't care about the type. For this reason, we can just catch Throwable (which is actually okay to do, with certain limitations) and not worry about its specific type.
  3. The remaining 1% of cases are the consequences of specific business requirements, which makes them part of the business (i.e. service) logic. If a business requirement is “When specifically error X happens, do Y”, writing service code that is tightly coupled to X being thrown is absolutely okay, as long as it’s implemented next to all the other business logic.

So, let’s switch from re-throwing exceptions to returning values. To do that, we need a datatype that denotes two situations:

  1. the code finished without issues, and the result is some value: T, and
  2. an oops happened

The first thing that comes to mind is Optional<T>, which is semantically equivalent to T?, however this is not rich enough for us. Nullables/optionals are fine for representing success, but if something goes wrong, we probably want to encode some information about what went wrong - the 'oops instance', so to speak. And nullables/optionals only have the ability to communicate "something" vs "nothing". What we want is Success(value: T) vs Failure(oops: Throwable).

So…let’s do exactly that! Let’s create a type Result<out T> and two subclasses Success<T>(val value: T): Result<T> and Failure(val error: Throwable) : Result<Nothing> and use those to wrap all of our calculations.

Hmmm…could we do better? Yes we could:

By introducing two simple types and a function, we were able to:

  • recover normal execution flow
  • get rid of spooky “action-at-a-distance” exception handlers
  • write code that is shorter, cleaner, and almost as simple as code where we completely ignore exceptions
  • deal with all possible errors, now and in the future
  • be explicit that this operation can fail, and use types to force calling code to deal with these failures (incidentally, this is what checked exceptions were trying, and mostly failing, to do)

However, most importantly, we have shifted our mindset. We no longer view exceptions as control flow constructs, we view them as simple carriers of information — basically data classes. We don’t immediately feel the need to translate or chain them, unless there is an actual need to provide more information or react to them — for the most part, the type won’t matter.

The benefit of this approach gets multiplied when you start dealing with libraries that represent errors in different ways.

Here’s an example:

Given those functions, we are tasked with creating ratioOfLibFuns() which returns the Int ratio of both library functions, and deals with errors appropriately. In this specific scenario, if the Int returned by libFun2 is missing, it means that the user forgot to do something, and we want to convey this information somehow.

Here’s how we would probably implement this up until now:

Ugh, what a mess. Thankfully, using what we just discovered, we can do better:

By introducing a super simple type, we managed to make to code about 4x shorter, increase its readability and maintainability, and become explicit about the fact that it can fail.

In fact, we could go further — we could combine this approach with what we learned about strongly typed illegal states, and avoid using UserRatioException completely:

Notice how much information you gain just by reading the signature of the function: it returns a Ratio instance, which recognizes a single type of business error (Int2Missing), and is wrapped in Result, which signifies that the method can fail unexpectedly. Without even looking at the implementation, you immediately know about all the things that can possibly happen in this method.

This is one of the incredibly powerful consequences of using types which have implicit meaning/behavior associated with them. You actually know this very well, but might not realize it. Think about it — Optional means a presence or absence of a value, List means multiple values, Future is a value which will exist at some point in time, Function is a value that can be produced, Result is a value or a failure, and so on.

This is actually one of the core principles of actual functional programming — using these datatypes, as well as many other ones such as Either, Writer, State, Eval and many, many others, to represent behavior in a program, and building what we need as a composition of these fundamental, provably correct behaviors. This topic is far beyond the scope of this article, but I thought it was worth mentioning that there was a close connection with what we’re doing here with Result.

It turns out that the Result hierarchy and the runCatching method are already part of the standard library. In the next article, we’ll explore what’s included in the standard library more closely.

Go back to Programming with Result: Motivation, jump to the Table of Contents, or continue to Programming with Result: kotlin.Result.

Java
Kotlin
Programming
Functional Programming
Exception Handling
Recommended from ReadMedium