Remove factory methods and expose Ok/Error constructors

Also converts them to data classes.
This commit is contained in:
Michael Bull 2017-10-22 15:05:02 +01:00
parent a9cf0593f9
commit dc4101b8ef
13 changed files with 114 additions and 144 deletions

View File

@ -4,12 +4,12 @@ package com.github.michaelbull.result
* - Rust: [Result.and](https://doc.rust-lang.org/std/result/enum.Result.html#method.and)
*
* @param result The [Result] to return if [Ok].
* @return The [result] if [Ok], otherwise [err].
* @return The [result] if [Ok], otherwise [Error].
*/
infix fun <V, E> Result<V, E>.and(result: Result<V, E>): Result<V, E> {
return when (this) {
is Ok -> result
is Error -> err(error)
is Error -> this
}
}
@ -18,11 +18,11 @@ infix fun <V, E> Result<V, E>.and(result: Result<V, E>): Result<V, E> {
* - Rust: [Result.and_then](https://doc.rust-lang.org/std/result/enum.Result.html#method.and_then)
*
* @param transform The transformation to apply to the [value][Ok.value].
* @return The [transformed][transform] [Result] if [Ok], otherwise [err].
* @return The [transformed][transform] [Result] if [Ok], otherwise [Error].
*/
infix inline fun <V, E, U> Result<V, E>.andThen(transform: (V) -> Result<U, E>): Result<U, E> {
return when (this) {
is Ok -> transform(value)
is Error -> err(error)
is Error -> this
}
}

View File

@ -20,11 +20,11 @@ inline fun <T, R, E> Iterable<T>.fold(
is Ok -> {
accumulator = operationResult.value
}
is Error -> return err(operationResult.error)
is Error -> return Error(operationResult.error)
}
}
return ok(accumulator)
return Ok(accumulator)
}
/**
@ -49,12 +49,12 @@ inline fun <T, R, E> List<T>.foldRight(
is Ok -> {
accumulator = operationResult.value
}
is Error -> return err(operationResult.error)
is Error -> return Error(operationResult.error)
}
}
}
return ok(accumulator)
return Ok(accumulator)
}
/**
@ -75,10 +75,10 @@ fun <V, E> combine(vararg results: Result<V, E>) = results.asIterable().combine(
* @return The combined [Result].
*/
fun <V, E> Iterable<Result<V, E>>.combine(): Result<List<V>, E> {
return ok(map {
return Ok(map {
when (it) {
is Ok -> it.value
is Error -> return err(it.error)
is Error -> return it
}
})
}

View File

@ -9,12 +9,12 @@ package com.github.michaelbull.result
* - Rust: [Result.map](https://doc.rust-lang.org/std/result/enum.Result.html#method.map)
*
* @param transform The transformation to apply to the [value][Ok.value]
* @return The [transformed][transform] [Result] if [Ok], otherwise [err].
* @return The [transformed][transform] [Result] if [Ok], otherwise [Error].
*/
infix inline fun <V, E, U> Result<V, E>.map(transform: (V) -> U): Result<U, E> {
return when (this) {
is Ok -> ok(transform(value))
is Error -> err(error)
is Ok -> Ok(transform(value))
is Error -> this
}
}
@ -31,8 +31,8 @@ infix inline fun <V, E, U> Result<V, E>.map(transform: (V) -> U): Result<U, E> {
*/
infix inline fun <V, E, F> Result<V, E>.mapError(transform: (E) -> F): Result<V, F> {
return when (this) {
is Ok -> ok(value)
is Error -> err(transform(error))
is Ok -> this
is Error -> Error(transform(error))
}
}
@ -74,7 +74,7 @@ inline fun <V, E, U, F> Result<V, E>.mapEither(
failure: (E) -> F
): Result<U, F> {
return when (this) {
is Ok -> ok(success(value))
is Error -> err(failure(error))
is Ok -> Ok(success(value))
is Error -> Error(failure(error))
}
}

View File

@ -4,11 +4,11 @@ package com.github.michaelbull.result
* - Rust: [Result.or](https://doc.rust-lang.org/std/result/enum.Result.html#method.or)
*
* @param result The [Result] to return if [Error].
* @return The [result] if [Error], otherwise [ok].
* @return The [result] if [Error], otherwise [Ok].
*/
infix fun <V, E> Result<V, E>.or(result: Result<V, E>): Result<V, E> {
return when (this) {
is Ok -> ok(value)
is Ok -> this
is Error -> result
}
}
@ -17,11 +17,11 @@ infix fun <V, E> Result<V, E>.or(result: Result<V, E>): Result<V, E> {
* - Rust: [Result.or_else](https://doc.rust-lang.org/std/result/enum.Result.html#method.or_else)
*
* @param transform The transformation to apply to the [error][Error.error].
* @return The [transformed][transform] [Result] if [Error], otherwise [ok].
* @return The [transformed][transform] [Result] if [Error], otherwise [Ok].
*/
infix inline fun <V, E> Result<V, E>.orElse(transform: (E) -> Result<V, E>): Result<V, E> {
return when (this) {
is Ok -> ok(this.value)
is Ok -> this
is Error -> transform(error)
}
}

View File

@ -9,37 +9,5 @@ package com.github.michaelbull.result
*/
sealed class Result<out V, out E>
fun <V> ok(value: V) = Ok<V, Nothing>(value)
fun <E> err(error: E) = Error<Nothing, E>(error)
class Ok<out V, out E> internal constructor(val value: V) : Result<V, E>() {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Ok<*, *>
if (value != other.value) return false
return true
}
override fun hashCode() = value?.hashCode() ?: 0
override fun toString() = "Result.Ok($value)"
}
class Error<out V, out E> internal constructor(val error: E) : Result<V, E>() {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Error<*, *>
if (error != other.error) return false
return true
}
override fun hashCode() = error?.hashCode() ?: 0
override fun toString() = "Result.Error($error)"
}
data class Ok<out V> constructor(val value: V) : Result<V, Nothing>()
data class Error<out E> constructor(val error: E) : Result<Nothing, E>()

View File

@ -10,25 +10,25 @@ internal class AndTest {
@Test
internal fun `and should return the result value if ok`() {
val value = ok(230).and(ok(500)).get()
val value = Ok(230).and(Ok(500)).get()
assertThat(value, equalTo(500))
}
@Test
internal fun `and should return the result value if not ok`() {
val error = ok(300).and(err("hello world")).getError()
val error = Ok(300).and(Error("hello world")).getError()
assertThat(error, equalTo("hello world"))
}
@Test
internal fun `andThen should return the transformed result value if ok`() {
val value = ok(5).andThen { ok(it + 7) }.get()
val value = Ok(5).andThen { Ok(it + 7) }.get()
assertThat(value, equalTo(12))
}
@Test
internal fun `andThen should return the result error if not ok`() {
val error = ok(20).andThen { ok(it + 43) }.andThen { err(AndError) }.getError()!!
val error = Ok(20).andThen { Ok(it + 43) }.andThen { Error(AndError) }.getError()!!
assertThat(error, sameInstance(AndError))
}
}

View File

@ -7,61 +7,61 @@ import org.junit.jupiter.api.Test
internal class GetTest {
@Test
internal fun `get should return the result value if ok`() {
val value = ok(12).get()
val value = Ok(12).get()
assertThat(value, equalTo(12))
}
@Test
internal fun `get should return null if not ok`() {
val value = err("error").get()
val value = Error("error").get()
assertThat(value, equalTo(null))
}
@Test
internal fun `getError should return null if ok`() {
val error = ok("example").getError()
val error = Ok("example").getError()
assertThat(error, equalTo(null))
}
@Test
internal fun `getError should return the result error if not ok`() {
val error = err("example").getError()
val error = Error("example").getError()
assertThat(error, equalTo("example"))
}
@Test
internal fun `getOr should return the result value if ok`() {
val value = ok("hello").getOr("world")
val value = Ok("hello").getOr("world")
assertThat(value, equalTo("hello"))
}
@Test
internal fun `getOr should return default value if not ok`() {
val value = err("error").getOr("default")
val value = Error("error").getOr("default")
assertThat(value, equalTo("default"))
}
@Test
internal fun `getErrorOr should return the default value if ok`() {
val error = ok("hello").getErrorOr("world")
val error = Ok("hello").getErrorOr("world")
assertThat(error, equalTo("world"))
}
@Test
internal fun `getErrorOr should return the result error if not ok`() {
val error = err("hello").getErrorOr("world")
val error = Error("hello").getErrorOr("world")
assertThat(error, equalTo("hello"))
}
@Test
internal fun `getOrElse should return the result value if ok`() {
val value = ok("hello").getOrElse { "world" }
val value = Ok("hello").getOrElse { "world" }
assertThat(value, equalTo("hello"))
}
@Test
internal fun `getOrElse should return the transformed result error if ok`() {
val value = err("hello").getOrElse { "world" }
val value = Error("hello").getOrElse { "world" }
assertThat(value, equalTo("world"))
}
}

View File

@ -20,7 +20,7 @@ internal class IterableTest {
internal fun `fold should return the accumulated value if ok`() {
val result = listOf(20, 30, 40, 50).fold(
initial = 10,
operation = { a, b -> ok(a + b) }
operation = { a, b -> Ok(a + b) }
)
result as Ok
@ -34,9 +34,9 @@ internal class IterableTest {
initial = 1,
operation = { a, b ->
when (b) {
(5 + 10) -> err(IterableError.IterableError1)
(5 + 10 + 15 + 20) -> err(IterableError.IterableError2)
else -> ok(a * b)
(5 + 10) -> Error(IterableError.IterableError1)
(5 + 10 + 15 + 20) -> Error(IterableError.IterableError2)
else -> Ok(a * b)
}
}
)
@ -51,7 +51,7 @@ internal class IterableTest {
internal fun `foldRight should return the accumulated value if ok`() {
val result = listOf(2, 5, 10, 20).foldRight(
initial = 100,
operation = { a, b -> ok(b - a) }
operation = { a, b -> Ok(b - a) }
)
result as Ok
@ -65,9 +65,9 @@ internal class IterableTest {
initial = 38500,
operation = { a, b ->
when (b) {
(((38500 / 40) / 20) / 10) -> err(IterableError.IterableError1)
((38500 / 40) / 20) -> err(IterableError.IterableError2)
else -> ok(b / a)
(((38500 / 40) / 20) / 10) -> Error(IterableError.IterableError1)
((38500 / 40) / 20) -> Error(IterableError.IterableError2)
else -> Ok(b / a)
}
}
)
@ -80,9 +80,9 @@ internal class IterableTest {
@Test
internal fun `combine should return the combined list of values if results are ok`() {
val values = combine(
ok(10),
ok(20),
ok(30)
Ok(10),
Ok(20),
Ok(30)
).get()!!
assertThat(values.size, equalTo(3))
@ -94,12 +94,12 @@ internal class IterableTest {
@Test
internal fun `combine should return the first error if results are not ok`() {
val result = combine(
ok(20),
ok(40),
err(IterableError.IterableError1),
ok(60),
err(IterableError.IterableError2),
ok(80)
Ok(20),
Ok(40),
Error(IterableError.IterableError1),
Ok(60),
Error(IterableError.IterableError2),
Ok(80)
)
result as Error
@ -110,12 +110,12 @@ internal class IterableTest {
@Test
internal fun `getAll should return all of the result values`() {
val values = getAll(
ok("hello"),
ok("big"),
err(IterableError.IterableError2),
ok("wide"),
err(IterableError.IterableError1),
ok("world")
Ok("hello"),
Ok("big"),
Error(IterableError.IterableError2),
Ok("wide"),
Error(IterableError.IterableError1),
Ok("world")
)
assertThat(values.size, equalTo(4))
@ -128,15 +128,15 @@ internal class IterableTest {
@Test
internal fun `getAllErrors should return all of the result errors`() {
val errors = getAllErrors(
err(IterableError.IterableError2),
ok("haskell"),
err(IterableError.IterableError2),
ok("f#"),
err(IterableError.IterableError1),
ok("elm"),
err(IterableError.IterableError1),
ok("clojure"),
err(IterableError.IterableError2)
Error(IterableError.IterableError2),
Ok("haskell"),
Error(IterableError.IterableError2),
Ok("f#"),
Error(IterableError.IterableError1),
Ok("elm"),
Error(IterableError.IterableError1),
Ok("clojure"),
Error(IterableError.IterableError2)
)
assertThat(errors.size, equalTo(5))
@ -150,15 +150,15 @@ internal class IterableTest {
@Test
internal fun `partition should return a pair of all the result values and errors`() {
val pairs = partition(
err(IterableError.IterableError2),
ok("haskell"),
err(IterableError.IterableError2),
ok("f#"),
err(IterableError.IterableError1),
ok("elm"),
err(IterableError.IterableError1),
ok("clojure"),
err(IterableError.IterableError2)
Error(IterableError.IterableError2),
Ok("haskell"),
Error(IterableError.IterableError2),
Ok("f#"),
Error(IterableError.IterableError1),
Ok("elm"),
Error(IterableError.IterableError1),
Ok("clojure"),
Error(IterableError.IterableError2)
)
val values = pairs.first

View File

@ -19,13 +19,15 @@ internal class MapTest {
@Test
internal fun `map should return the transformed result value if ok`() {
val value = ok(10).map { it + 20 }.get()
val test: Result<Int, Nothing> = Ok(500).map { 50 }
val value = Ok(10).map { it + 20 }.get()
assertThat(value, equalTo(30))
}
@Test
internal fun `map should return the result error if not ok`() {
val result = err(MapError.HelloError).map { "hello $it" }
val result = Error(MapError.HelloError).map { "hello $it" }
result as Error
@ -34,18 +36,18 @@ internal class MapTest {
@Test
internal fun `mapError should return the result value if ok`() {
val value = ok(55).map { it + 15 }.mapError { MapError.WorldError }.get()
val value = Ok(55).map { it + 15 }.mapError { MapError.WorldError }.get()
assertThat(value, equalTo(70))
}
@Test
internal fun `mapError should return the transformed result error if not ok`() {
val result: Result<String, MapError> = ok("let")
val result: Result<String, MapError> = Ok("let")
.map { "$it me" }
.andThen {
when (it) {
"let me" -> err(MapError.CustomError("$it $it"))
else -> ok("$it get")
"let me" -> Error(MapError.CustomError("$it $it"))
else -> Ok("$it get")
}
}
.mapError { MapError.CustomError("${it.reason} get what i want") }
@ -57,7 +59,7 @@ internal class MapTest {
@Test
internal fun `mapBoth should return the transformed result value if ok`() {
val value = ok("there is").mapBoth(
val value = Ok("there is").mapBoth(
success = { "$it a light" },
failure = { "$it that never" }
)
@ -67,7 +69,7 @@ internal class MapTest {
@Test
internal fun `mapBoth should return the transformed result error if not ok`() {
val error = err(MapError.CustomError("this")).mapBoth(
val error = Error(MapError.CustomError("this")).mapBoth(
success = { "$it charming" },
failure = { "${it.reason} man" }
)
@ -77,7 +79,7 @@ internal class MapTest {
@Test
internal fun `mapEither should return the transformed result value if ok`() {
val result = ok(500).mapEither(
val result = Ok(500).mapEither(
success = { it + 500 },
failure = { MapError.CustomError("$it") }
)
@ -89,7 +91,7 @@ internal class MapTest {
@Test
internal fun `mapEither should return the transformed result error if not ok`() {
val result = err("the reckless").mapEither(
val result = Error("the reckless").mapEither(
success = { "the wild youth" },
failure = { MapError.CustomError("the truth") }
)

View File

@ -11,28 +11,28 @@ internal class OnTest {
@Test
internal fun `onSuccess should invoke the callback when result is ok`() {
val counter = Counter(50)
ok(counter).onSuccess { it.count += 50 }
Ok(counter).onSuccess { it.count += 50 }
assertThat(counter.count, equalTo(100))
}
@Test
internal fun `onSuccess should not invoke the callback when result is not ok`() {
val counter = Counter(200)
err(CounterError).onSuccess { counter.count -= 50 }
Error(CounterError).onSuccess { counter.count -= 50 }
assertThat(counter.count, equalTo(200))
}
@Test
internal fun `onFailure should invoke the callback when result is not ok`() {
val counter = Counter(555)
err(CounterError).onFailure { counter.count += 100 }
Error(CounterError).onFailure { counter.count += 100 }
assertThat(counter.count, equalTo(655))
}
@Test
internal fun `onFailure should not invoke the callback when result is ok`() {
val counter = Counter(1020)
ok("hello").onFailure { counter.count = 1030 }
Ok("hello").onFailure { counter.count = 1030 }
assertThat(counter.count, equalTo(1020))
}
}

View File

@ -9,25 +9,25 @@ internal class OrTest {
@Test
internal fun `or should return the result value if ok`() {
val value = ok(500).or(ok(1000)).get()
val value = Ok(500).or(Ok(1000)).get()
assertThat(value, equalTo(500))
}
@Test
internal fun `or should return the default value if not ok`() {
val value = err(OrError).or(ok(5000)).get()
val value = Error(OrError).or(Ok(5000)).get()
assertThat(value, equalTo(5000))
}
@Test
internal fun `orElse should return the result value if ok`() {
val value = ok(3000).orElse { ok(4000) }.get()
val value = Ok(3000).orElse { Ok(4000) }.get()
assertThat(value, equalTo(3000))
}
@Test
internal fun `orElse should return the transformed value if not ok`() {
val value = err(4000).orElse { ok(2000) }.get()
val value = Error(4000).orElse { Ok(2000) }.get()
assertThat(value, equalTo(2000))
}
}

View File

@ -8,32 +8,32 @@ import org.junit.jupiter.api.Test
internal class ResultIteratorTest {
@Test
internal fun `hasNext should return true if unyielded and result is ok`() {
val iterator = ok("hello").iterator()
val iterator = Ok("hello").iterator()
assertThat(iterator.hasNext(), equalTo(true))
}
@Test
internal fun `hasNext should return false if result is not ok`() {
val iterator = err("hello").iterator()
val iterator = Error("hello").iterator()
assertThat(iterator.hasNext(), equalTo(false))
}
@Test
internal fun `hasNext should return false if yielded`() {
val iterator = ok("hello").iterator()
val iterator = Ok("hello").iterator()
iterator.next()
assertThat(iterator.hasNext(), equalTo(false))
}
@Test
internal fun `next should return the result value if unyielded and result is ok`() {
val value = ok("hello").iterator().next()
val value = Ok("hello").iterator().next()
assertThat(value, equalTo("hello"))
}
@Test
internal fun `next should throw NoSuchElementException if unyielded and result is not ok`() {
val iterator = err("hello").iterator()
val iterator = Error("hello").iterator()
assertThrows(NoSuchElementException::class.java) {
iterator.next()
@ -42,7 +42,7 @@ internal class ResultIteratorTest {
@Test
internal fun `next should throw NoSuchElementException if yielded and result is ok`() {
val iterator = ok("hello").iterator()
val iterator = Ok("hello").iterator()
iterator.next()
assertThrows(NoSuchElementException::class.java) {
@ -52,14 +52,14 @@ internal class ResultIteratorTest {
@Test
internal fun `remove should make hasNext return false`() {
val iterator = ok("hello").mutableIterator()
val iterator = Ok("hello").mutableIterator()
iterator.remove()
assertThat(iterator.hasNext(), equalTo(false))
}
@Test
internal fun `remove should make next throw NoSuchElementException`() {
val iterator = ok("hello").mutableIterator()
val iterator = Ok("hello").mutableIterator()
iterator.remove()
assertThrows(NoSuchElementException::class.java) {

View File

@ -8,14 +8,14 @@ import org.junit.jupiter.api.Test
internal class UnwrapTest {
@Test
internal fun `unwrap should return the result value if ok`() {
val value = ok(5000).unwrap()
val value = Ok(5000).unwrap()
assertThat(value, equalTo(5000))
}
@Test
internal fun `unwrap should throw an UnwrapException if not ok`() {
val throwable = assertThrows(UnwrapException::class.java, {
err(5000).unwrap()
Error(5000).unwrap()
})
assertThat(throwable.message, equalTo("called Result.wrap on an Error value 5000"))
@ -23,14 +23,14 @@ internal class UnwrapTest {
@Test
internal fun `expect should return the result value if ok`() {
val value = ok(1994).expect("the year should be")
val value = Ok(1994).expect("the year should be")
assertThat(value, equalTo(1994))
}
@Test
internal fun `expect should throw an UnwrapException with a specified message if not ok`() {
val throwable = assertThrows(UnwrapException::class.java, {
err(1994).expect("the year should be")
Error(1994).expect("the year should be")
})
assertThat(throwable.message, equalTo("the year should be 1994"))
@ -39,7 +39,7 @@ internal class UnwrapTest {
@Test
internal fun `unwrapError should throw an UnwrapException if ok`() {
val throwable = assertThrows(UnwrapException::class.java, {
ok("example").unwrapError()
Ok("example").unwrapError()
})
assertThat(throwable.message, equalTo("called Result.unwrapError on an Ok value example"))
@ -47,14 +47,14 @@ internal class UnwrapTest {
@Test
internal fun `unwrapError should return the result error if not ok`() {
val error = err("example").unwrapError()
val error = Error("example").unwrapError()
assertThat(error, equalTo("example"))
}
@Test
internal fun `expectError should throw an UnwrapException with a specified message if ok`() {
val throwable = assertThrows(UnwrapException::class.java, {
ok(2010).expectError("the year should be")
Ok(2010).expectError("the year should be")
})
assertThat(throwable.message, equalTo("the year should be 2010"))
@ -62,7 +62,7 @@ internal class UnwrapTest {
@Test
internal fun `expectError should return the result error if not ok`() {
val error = err(2010).expectError("the year should be")
val error = Error(2010).expectError("the year should be")
assertThat(error, equalTo(2010))
}
}