All Projects → kittinunf → Result

kittinunf / Result

Licence: mit
The modelling for success/failure of operations in Kotlin

Programming Languages

kotlin
9241 projects

Projects that are alternatives of or similar to Result

Kotlin Flow Extensions
Extensions to the Kotlin Flow library.
Stars: ✭ 404 (-42.7%)
Mutual labels:  functional
Hof
Higher-order functions for c++
Stars: ✭ 467 (-33.76%)
Mutual labels:  functional
Linq
Linq for list comprehension in C++
Stars: ✭ 599 (-15.04%)
Mutual labels:  functional
Enso
Hybrid visual and textual functional programming.
Stars: ✭ 5,238 (+642.98%)
Mutual labels:  functional
Rxswift
Reactive Programming in Swift
Stars: ✭ 21,163 (+2901.84%)
Mutual labels:  functional
Moses
Utility library for functional programming in Lua
Stars: ✭ 541 (-23.26%)
Mutual labels:  functional
Cloe
Cloe programming language
Stars: ✭ 398 (-43.55%)
Mutual labels:  functional
Cljfx
Declarative, functional and extensible wrapper of JavaFX inspired by better parts of react and re-frame
Stars: ✭ 624 (-11.49%)
Mutual labels:  functional
Async Reactor
Render async Stateless Functional Components in React
Stars: ✭ 452 (-35.89%)
Mutual labels:  functional
Fkit
A functional programming toolkit for JavaScript.
Stars: ✭ 588 (-16.6%)
Mutual labels:  functional
Orleankka
Functional API for Microsoft Orleans http://orleanscontrib.github.io/Orleankka/
Stars: ✭ 429 (-39.15%)
Mutual labels:  functional
Bubbletea
A powerful little TUI framework 🏗
Stars: ✭ 7,886 (+1018.58%)
Mutual labels:  functional
Pampy.js
Pampy.js: Pattern Matching for JavaScript
Stars: ✭ 544 (-22.84%)
Mutual labels:  functional
Retry
A simple, stateless, functional mechanism to perform actions repetitively until successful.
Stars: ✭ 406 (-42.41%)
Mutual labels:  functional
Luna Studio
Looking for Luna, the WYSIWYG language for data processing? Development has moved 👉
Stars: ✭ 602 (-14.61%)
Mutual labels:  functional
Yalinqo
Yet Another LINQ to Objects for PHP [Simplified BSD]
Stars: ✭ 400 (-43.26%)
Mutual labels:  functional
Jsonnet
Jsonnet - The data templating language
Stars: ✭ 5,257 (+645.67%)
Mutual labels:  functional
Meta Typing
📚 Functions and algorithms implemented purely with TypeScript's type system
Stars: ✭ 628 (-10.92%)
Mutual labels:  functional
Spock
Another Haskell web framework for rapid development
Stars: ✭ 623 (-11.63%)
Mutual labels:  functional
Functional Programming Learning Path
A Learning Path for Functional Programming
Stars: ✭ 582 (-17.45%)
Mutual labels:  functional

Result

Kotlin jcenter MavenCentral Build Status Codecov

This is a tiny framework for modelling success/failure of operations in Kotlin. In short, it is a model in type of Result<V: Any?, E : Exception>.

Ideology

Result<V: Any?, E: Exception> is to provide higher abstraction of operation that can be ended with result either success or failure. Result.Success represents value in case of success, and Result.Failure represents error in case of failure which is upper bounded with Exception type.

Installation

Gradle

repositories {
    jcenter() //or mavenCentral()
}

dependencies {
    compile 'com.github.kittinunf.result:result:<latest-version>' //for jvm
    compile 'com.github.kittinunf.result:result-coroutines:<latest-version>' //for kotlin's coroutine support
}

TL;DR

This model is highly inspired by "Railway Oriented Programming" concept.

Result allows one to express series of success/failure operations in Kotlin as;

Result.of(operation)
      .flatMap { normalizedData(it) }
      .map { createRequestFromData(it) }
      .flatMap { database.updateFromRequest(it) }

Work with Result is easy

//multi-declaration
val (value, error) = result

//get
val value: Int = result.get<Int>() ?: 0
val ex: Exception = result.get<Exception>()!!

//success
result.success {
}

//failure
result.failure {
}

//fold is there, if you want to handle both success and failure
result.fold({ value ->
    //do something with value
}, { error ->
    //do something with error
})

Combine several results in a validation (without stopping at the first error)

val r1: Result<Int, Exception> = Result.of(1)
val r2: Result<Int, Exception> = Result.of{throw Exception("Not a number")}
val r3: Result<Int, Exception> = Result.of(3)
val r4: Result<Int, Exception> = Result.of{throw Exception("Division by zero")}

val validation = Validation(r1, r2, r3, r4)
validation.hasFailure //true
validation.failures.map{it.message} //[Not a number, Division by zero]

Why

Result is suitable whenever there is a need to represent an operation that has the possibility of failure. Error handling can be cumbersome to work with. Result helps process the operations in a nice, functional way, while maintaining readability to your code.

Let's consider a need to read data from foo, and to perform some further validation

fun process(): String {
    try {
        val foo = File("/path/to/file/foo.txt").readText()
        val isSuccessful = processData(foo)
        if (!isSuccessful) {
            return "Data is corrupted and cannot be processed"
        }
    } catch (e: Exception) {
        //do something if error
        Logger.log(ERROR, e.message())
    }
}

However, things start getting ugly when we have chain of operations being run sequentially, such as

fun process(): String {
    try {
        val foo = File("/path/to/file/foo.txt").readText()
        val isSuccessful = normalizedData(foo)
        if (!isSuccessful) {
            return "Data cannot be processable"
        }
        val request = createRequestFromData(foo)
        try {
            val result = database.updateFromRequest(request)
            if (!result) {
                return "Record in DB is not found"
            }
        } catch (dbEx: DBException) {
            return "DB error, cannot update"
        }
    } catch (e: Exception) {
        //do something if error
        Logger.log(ERROR, e.message())
    }
}

Ouch, it looks pretty bleak.

Let's see how Result can help us.

First, we break things down into a small set of model in Result.

  • Read a file
val operation = { File("/path/to/file/foo.txt").readText() }
Result.of(operation)  // Result<String, FileException>
  • Normalize a data
fun normalizedData(foo): Result<Boolean, NormalizedException> {
    Result.of(foo.normalize())
}
  • Create a request from data
fun createRequestFromData(foo): Request {
    return createRequest(foo)
}
  • Update DB with Request
fun database.updateFromRequest(request): Result<Boolean, DBException> {
    val transaction = request.transaction
    return Result.of(db.openTransaction {
        val success = db.execute(transaction)
        if (!success) {
            throw DBException("Error")
        }
        return success
    })
}

The whole operation can be chained by the following;

Result.of(operation)
      .flatMap { normalizedData(it) }
      .map { createRequestFromData(it) }
      .flatMap { database.updateFromRequest(it) }

The creates a nice "happy path" of the whole chain, also handle error as appropriate. It looks better and cleaner, right?.

Never Fail Operation

In some case, one wants to model an always successful operation. Result<V: Any?, NoException> is a good idea for that. NoException is to indicate that there is no exception to throw. E.g.

// Add operation can never be failure
fun add(i: Int, j: Int) : Result<Int, NoException>

Nice thing about modelling in this way is to be able to compose it with others "failable" operations in Result.

High Order functions

Success

map and flatMap

map transforms Result with given transformation (V) -> U. As a result, we are able to transform V into a new V in the case where Result is Result.Success. When Result is Result.Failure, error is re-wrapped into a new Result.

flatMap is similar to map, however it requires transformation in type of (V) -> Result<U, ...>.

Failure

mapError and flatMapError

mapError ((E) -> E2) and flatMapError ((E) -> Result<E2, ...>) are counterpart of map and flatMap. However, they are operate on Result.Failure. It is quite handy when one needs to do some transformation on given Exception into a custom type of Exception that suits ones' need.

Support for Kotlin's Coroutines

SuspendableResult & SuspendableValidation

These classes are an exact copy of the Result and Validation classes respectively. Use these classes if you are planning on using coroutines in your functions.

Railway Oriented Programming

If interested, here are more articles that one might enjoy.

Credit to Scott Wlaschin

Credits

Result is brought to you by contributors.

License

Result is released under the MIT license.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].