Refactored ErrorOr into Try, with Success and Failure data sub-classes, and moved it into core.utilities

This commit is contained in:
Shams Asari
2017-07-10 12:16:00 +01:00
parent 7e8de79848
commit 7caee508ec
19 changed files with 216 additions and 209 deletions

View File

@ -23,7 +23,10 @@ import java.nio.file.*
import java.nio.file.attribute.FileAttribute
import java.time.Duration
import java.time.temporal.Temporal
import java.util.concurrent.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
import java.util.stream.Stream
import java.util.zip.Deflater
@ -324,63 +327,6 @@ data class InputStreamAndHash(val inputStream: InputStream, val sha256: SecureHa
val Throwable.rootCause: Throwable get() = Throwables.getRootCause(this)
/** Representation of an operation that may have thrown an error. */
@Suppress("DataClassPrivateConstructor")
@CordaSerializable
data class ErrorOr<out A> private constructor(val value: A?, val error: Throwable?) {
// The ErrorOr holds a value iff error == null
constructor(value: A) : this(value, null)
companion object {
/** Runs the given lambda and wraps the result. */
inline fun <T> catch(body: () -> T): ErrorOr<T> {
return try {
ErrorOr(body())
} catch (t: Throwable) {
ErrorOr.of(t)
}
}
fun of(t: Throwable) = ErrorOr(null, t)
}
fun <T> match(onValue: (A) -> T, onError: (Throwable) -> T): T {
if (error == null) {
return onValue(value as A)
} else {
return onError(error)
}
}
fun getOrThrow(): A {
if (error == null) {
return value as A
} else {
throw error
}
}
// Functor
fun <B> map(function: (A) -> B) = ErrorOr(value?.let(function), error)
// Applicative
fun <B, C> combine(other: ErrorOr<B>, function: (A, B) -> C): ErrorOr<C> {
val newError = error ?: other.error
return ErrorOr(if (newError != null) null else function(value as A, other.value as B), newError)
}
// Monad
fun <B> bind(function: (A) -> ErrorOr<B>): ErrorOr<B> {
return if (error == null) {
function(value as A)
} else {
ErrorOr.of(error)
}
}
fun mapError(function: (Throwable) -> Throwable) = ErrorOr(value, error?.let(function))
}
/**
* Returns an Observable that buffers events until subscribed.
* @see UnicastSubject

View File

@ -1,7 +1,6 @@
package net.corda.core.messaging
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.ErrorOr
import net.corda.core.contracts.Amount
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
@ -19,6 +18,7 @@ import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.node.services.vault.Sort
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.Try
import org.bouncycastle.asn1.x500.X500Name
import rx.Observable
import java.io.InputStream
@ -44,7 +44,7 @@ sealed class StateMachineUpdate {
override val id: StateMachineRunId get() = stateMachineInfo.id
}
data class Removed(override val id: StateMachineRunId, val result: ErrorOr<*>) : StateMachineUpdate()
data class Removed(override val id: StateMachineRunId, val result: Try<*>) : StateMachineUpdate()
}
@CordaSerializable

View File

@ -0,0 +1,74 @@
package net.corda.core.utilities
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.Try.Failure
import net.corda.core.utilities.Try.Success
/**
* Representation of an operation that has either succeeded with a result (represented by [Success]) or failed with an
* exception (represented by [Failure]).
*/
@CordaSerializable
sealed class Try<out A> {
companion object {
/**
* Executes the given block of code and returns a [Success] capturing the result, or a [Failure] if an exception
* is thrown.
*/
@JvmStatic
inline fun <T> on(body: () -> T): Try<T> {
return try {
Success(body())
} catch (t: Throwable) {
Failure(t)
}
}
}
/** Returns `true` iff the [Try] is a [Success]. */
abstract val isFailure: Boolean
/** Returns `true` iff the [Try] is a [Failure]. */
abstract val isSuccess: Boolean
/** Returns the value if a [Success] otherwise throws the exception if a [Failure]. */
abstract fun getOrThrow(): A
/** Maps the given function to the value from this [Success], or returns `this` if this is a [Failure]. */
inline fun <B> map(function: (A) -> B): Try<B> = when (this) {
is Success -> Success(function(value))
is Failure -> this
}
/** Returns the given function applied to the value from this [Success], or returns `this` if this is a [Failure]. */
inline fun <B> flatMap(function: (A) -> Try<B>): Try<B> = when (this) {
is Success -> function(value)
is Failure -> this
}
/**
* Maps the given function to the values from this [Success] and [other], or returns `this` if this is a [Failure]
* or [other] if [other] is a [Failure].
*/
inline fun <B, C> combine(other: Try<B>, function: (A, B) -> C): Try<C> = when (this) {
is Success -> when (other) {
is Success -> Success(function(value, other.value))
is Failure -> other
}
is Failure -> this
}
data class Success<out A>(val value: A) : Try<A>() {
override val isSuccess: Boolean get() = true
override val isFailure: Boolean get() = false
override fun getOrThrow(): A = value
override fun toString(): String = "Success($value)"
}
data class Failure(val exception: Throwable) : Try<Nothing>() {
override val isSuccess: Boolean get() = false
override val isFailure: Boolean get() = true
override fun getOrThrow(): Nothing = throw exception
override fun toString(): String = "Failure($exception)"
}
}