From 7218a5c0a57571f9c796462fca9cf042bc045acf Mon Sep 17 00:00:00 2001 From: JMPergar Date: Fri, 24 Mar 2017 13:42:56 +0100 Subject: [PATCH 1/2] Create Try type and base combinators --- core/src/main/java/kats/Try.kt | 144 +++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 core/src/main/java/kats/Try.kt diff --git a/core/src/main/java/kats/Try.kt b/core/src/main/java/kats/Try.kt new file mode 100644 index 00000000000..5fa692eda39 --- /dev/null +++ b/core/src/main/java/kats/Try.kt @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kats + +/** + * The `Try` type represents a computation that may either result in an exception, or return a + * successfully computed value. + * + * Port of https://github.com/scala/scala/blob/v2.12.1/src/library/scala/util/Try.scala + */ +sealed class Try { + + /** + * Returns `true` if the `Try` is a `Failure`, `false` otherwise. + */ + abstract val isFailure: Boolean + + /** + * Returns `true` if the `Try` is a `Success`, `false` otherwise. + */ + abstract val isSuccess: Boolean + + /** + * Returns the value from this `Success` or the given `default` argument if this is a `Failure`. + * + * ''Note:'': This will throw an exception if it is not a success and default throws an exception. + */ + abstract fun getOrElse(default: () -> A): A + + /** + * Returns the value from this `Success` or throws the exception if this is a `Failure`. + */ + abstract fun get(): A + + /** + * Returns the given function applied to the value from this `Success` or returns this if this is a `Failure`. + */ + abstract fun flatMap(f: (A) -> Try): Try + + /** + * Maps the given function to the value from this `Success` or returns this if this is a `Failure`. + */ + abstract fun map(f: (A) -> B): Try + + /** + * Applies the given function `f` if this is a `Success`, otherwise returns `Unit` if this is a `Failure`. + * + * ''Note:'' If `f` throws, then this method may throw an exception. + */ + abstract fun foreach(f: (A) -> B): Unit + + /** + * Converts this to a `Failure` if the predicate is not satisfied. + */ + abstract fun filter(p: (A) -> Boolean): Try + + /** + * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. + * This is like `flatMap` for the exception. + */ + abstract fun recoverWith(f: (Throwable) -> Try): Try + + /** + * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. + * This is like map for the exception. + */ + abstract fun recover(f: (Throwable) -> A): Try + + /** + * Inverts this `Try`. If this is a `Failure`, returns its exception wrapped in a `Success`. + * If this is a `Success`, returns a `Failure` containing an `UnsupportedOperationException`. + */ + abstract fun failed(): Try + + /** + * Completes this `Try` by applying the function `f` to this if this is of type `Failure`, + * or conversely, by applying `s` if this is a `Success`. + */ + abstract fun transform(s: (A) -> Try, f: (Throwable) -> Try): Try + + /** + * Applies `fa` if this is a `Failure` or `fb` if this is a `Success`. + * If `fb` is initially applied and throws an exception, + * then `fa` is applied with this exception. + */ + abstract fun fold(fa: (Throwable) -> B, fb: (A) -> B): B + + /** + * The `Failure` type represents a computation that result in an exception. + */ + class Failure(val exception: Throwable) : Try() { + override val isFailure: Boolean = false + override val isSuccess: Boolean = true + override fun get(): A = throw exception + override fun getOrElse(default: () -> A): A = default() + override fun flatMap(f: (A) -> Try): Try = Failure(exception) + override fun map(f: (A) -> B): Try = Failure(exception) + override fun foreach(f: (A) -> B): Unit { } + override fun filter(p: (A) -> Boolean): Try = this + override fun recover(f: (Throwable) -> A): Try = + try { Success(f(exception)) } catch(e: Throwable) { Failure(e) } + override fun recoverWith(f: (Throwable) -> Try): Try = + try { f(exception) } catch(e: Throwable) { Failure(e) } + override fun failed(): Try = Success(exception) + override fun transform(s: (A) -> Try, f: (Throwable) -> Try): Try = + try { f(exception) } catch(e: Throwable) { Failure(e) } + override fun fold(fa: (Throwable) -> B, fb: (A) -> B): B = fa(exception) + } + + /** + * The `Success` type represents a computation that return a successfully computed value. + */ + class Success(val value: A) : Try() { + override val isFailure: Boolean = true + override val isSuccess: Boolean = false + override fun get(): A = value + override fun getOrElse(default: () -> A): A = get() + override fun flatMap(f: (A) -> Try): Try = try { f(value) } catch(e: Throwable) { Failure(e) } + override fun map(f: (A) -> B): Try = try { Success(f(value)) } catch(e: Throwable) { Failure(e) } + override fun foreach(f: (A) -> B): Unit { f(value) } + override fun filter(p: (A) -> Boolean): Try = + try { if (p(value)) this else Failure(NoSuchElementException("Predicate does not hold for " + value)) } + catch(e: Throwable) { Failure(e) } + override fun recover(f: (Throwable) -> A): Try = this + override fun recoverWith(f: (Throwable) -> Try): Try = this + override fun failed(): Try = Failure(UnsupportedOperationException("Success.failed")) + override fun transform(s: (A) -> Try, f: (Throwable) -> Try): Try = flatMap(s) + override fun fold(fa: (Throwable) -> B, fb: (A) -> B): B = try { fb(value) } catch(e: Throwable) { fa(e) } + } +} \ No newline at end of file From 1f484b3636247fda62c482ce8e1ade13db9b5037 Mon Sep 17 00:00:00 2001 From: JMPergar Date: Fri, 24 Mar 2017 18:23:15 +0100 Subject: [PATCH 2/2] Implement methods delegating to fold methods --- core/src/main/java/kats/Try.kt | 144 ------------------------------- kats/src/main/kotlin/kats/Try.kt | 119 +++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 144 deletions(-) delete mode 100644 core/src/main/java/kats/Try.kt create mode 100644 kats/src/main/kotlin/kats/Try.kt diff --git a/core/src/main/java/kats/Try.kt b/core/src/main/java/kats/Try.kt deleted file mode 100644 index 5fa692eda39..00000000000 --- a/core/src/main/java/kats/Try.kt +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2017 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kats - -/** - * The `Try` type represents a computation that may either result in an exception, or return a - * successfully computed value. - * - * Port of https://github.com/scala/scala/blob/v2.12.1/src/library/scala/util/Try.scala - */ -sealed class Try { - - /** - * Returns `true` if the `Try` is a `Failure`, `false` otherwise. - */ - abstract val isFailure: Boolean - - /** - * Returns `true` if the `Try` is a `Success`, `false` otherwise. - */ - abstract val isSuccess: Boolean - - /** - * Returns the value from this `Success` or the given `default` argument if this is a `Failure`. - * - * ''Note:'': This will throw an exception if it is not a success and default throws an exception. - */ - abstract fun getOrElse(default: () -> A): A - - /** - * Returns the value from this `Success` or throws the exception if this is a `Failure`. - */ - abstract fun get(): A - - /** - * Returns the given function applied to the value from this `Success` or returns this if this is a `Failure`. - */ - abstract fun flatMap(f: (A) -> Try): Try - - /** - * Maps the given function to the value from this `Success` or returns this if this is a `Failure`. - */ - abstract fun map(f: (A) -> B): Try - - /** - * Applies the given function `f` if this is a `Success`, otherwise returns `Unit` if this is a `Failure`. - * - * ''Note:'' If `f` throws, then this method may throw an exception. - */ - abstract fun foreach(f: (A) -> B): Unit - - /** - * Converts this to a `Failure` if the predicate is not satisfied. - */ - abstract fun filter(p: (A) -> Boolean): Try - - /** - * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. - * This is like `flatMap` for the exception. - */ - abstract fun recoverWith(f: (Throwable) -> Try): Try - - /** - * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. - * This is like map for the exception. - */ - abstract fun recover(f: (Throwable) -> A): Try - - /** - * Inverts this `Try`. If this is a `Failure`, returns its exception wrapped in a `Success`. - * If this is a `Success`, returns a `Failure` containing an `UnsupportedOperationException`. - */ - abstract fun failed(): Try - - /** - * Completes this `Try` by applying the function `f` to this if this is of type `Failure`, - * or conversely, by applying `s` if this is a `Success`. - */ - abstract fun transform(s: (A) -> Try, f: (Throwable) -> Try): Try - - /** - * Applies `fa` if this is a `Failure` or `fb` if this is a `Success`. - * If `fb` is initially applied and throws an exception, - * then `fa` is applied with this exception. - */ - abstract fun fold(fa: (Throwable) -> B, fb: (A) -> B): B - - /** - * The `Failure` type represents a computation that result in an exception. - */ - class Failure(val exception: Throwable) : Try() { - override val isFailure: Boolean = false - override val isSuccess: Boolean = true - override fun get(): A = throw exception - override fun getOrElse(default: () -> A): A = default() - override fun flatMap(f: (A) -> Try): Try = Failure(exception) - override fun map(f: (A) -> B): Try = Failure(exception) - override fun foreach(f: (A) -> B): Unit { } - override fun filter(p: (A) -> Boolean): Try = this - override fun recover(f: (Throwable) -> A): Try = - try { Success(f(exception)) } catch(e: Throwable) { Failure(e) } - override fun recoverWith(f: (Throwable) -> Try): Try = - try { f(exception) } catch(e: Throwable) { Failure(e) } - override fun failed(): Try = Success(exception) - override fun transform(s: (A) -> Try, f: (Throwable) -> Try): Try = - try { f(exception) } catch(e: Throwable) { Failure(e) } - override fun fold(fa: (Throwable) -> B, fb: (A) -> B): B = fa(exception) - } - - /** - * The `Success` type represents a computation that return a successfully computed value. - */ - class Success(val value: A) : Try() { - override val isFailure: Boolean = true - override val isSuccess: Boolean = false - override fun get(): A = value - override fun getOrElse(default: () -> A): A = get() - override fun flatMap(f: (A) -> Try): Try = try { f(value) } catch(e: Throwable) { Failure(e) } - override fun map(f: (A) -> B): Try = try { Success(f(value)) } catch(e: Throwable) { Failure(e) } - override fun foreach(f: (A) -> B): Unit { f(value) } - override fun filter(p: (A) -> Boolean): Try = - try { if (p(value)) this else Failure(NoSuchElementException("Predicate does not hold for " + value)) } - catch(e: Throwable) { Failure(e) } - override fun recover(f: (Throwable) -> A): Try = this - override fun recoverWith(f: (Throwable) -> Try): Try = this - override fun failed(): Try = Failure(UnsupportedOperationException("Success.failed")) - override fun transform(s: (A) -> Try, f: (Throwable) -> Try): Try = flatMap(s) - override fun fold(fa: (Throwable) -> B, fb: (A) -> B): B = try { fb(value) } catch(e: Throwable) { fa(e) } - } -} \ No newline at end of file diff --git a/kats/src/main/kotlin/kats/Try.kt b/kats/src/main/kotlin/kats/Try.kt new file mode 100644 index 00000000000..4f59bd609e4 --- /dev/null +++ b/kats/src/main/kotlin/kats/Try.kt @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2017 The Kats Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kats + +/** + * The `Try` type represents a computation that may either result in an exception, or return a + * successfully computed value. + * + * Port of https://github.com/scala/scala/blob/v2.12.1/src/library/scala/util/Try.scala + */ +sealed class Try { + + companion object { + + inline operator fun invoke(f: () -> A): Try = try { Success(f()) } catch (e: Throwable) { Failure(e) } + } + + /** + * Returns `true` if the `Try` is a `Failure`, `false` otherwise. + */ + abstract val isFailure: Boolean + + /** + * Returns `true` if the `Try` is a `Success`, `false` otherwise. + */ + abstract val isSuccess: Boolean + + /** + * Returns the given function applied to the value from this `Success` or returns this if this is a `Failure`. + */ + inline fun flatMap(crossinline f: (A) -> Try): Try = fold({ Failure(it) }, { f(it) }) + + /** + * Maps the given function to the value from this `Success` or returns this if this is a `Failure`. + */ + inline fun map(crossinline f: (A) -> B): Try = fold({ Failure(it) }, { Success(f(it)) }) + + /** + * Converts this to a `Failure` if the predicate is not satisfied. + */ + inline fun filter(crossinline p: (A) -> Boolean): Try = fold( + { Failure(it) }, + { if (p(it)) Success(it) else Failure(NoSuchElementException("Predicate does not hold for " + it)) } + ) + + /** + * Inverts this `Try`. If this is a `Failure`, returns its exception wrapped in a `Success`. + * If this is a `Success`, returns a `Failure` containing an `UnsupportedOperationException`. + */ + fun failed(): Try = fold( + { Success(it) }, + { Failure(UnsupportedOperationException("Success.failed")) } + ) + + /** + * Applies `fa` if this is a `Failure` or `fb` if this is a `Success`. + * If `fb` is initially applied and throws an exception, + * then `fa` is applied with this exception. + */ + fun fold(fa: (Throwable) -> B, fb: (A) -> B): B = when(this) { + is Failure -> fa(exception) + is Success -> try { fb(value) } catch(e: Throwable) { fa(e) } + } + + /** + * The `Failure` type represents a computation that result in an exception. + */ + class Failure(val exception: Throwable) : Try() { + override val isFailure: Boolean = false + override val isSuccess: Boolean = true + } + + /** + * The `Success` type represents a computation that return a successfully computed value. + */ + class Success(val value: A) : Try() { + override val isFailure: Boolean = true + override val isSuccess: Boolean = false + } +} + +/** + * Returns the value from this `Success` or the given `default` argument if this is a `Failure`. + * + * ''Note:'': This will throw an exception if it is not a success and default throws an exception. + */ +fun Try.getOrElse(default: () -> B): B = fold({ default() }, { b -> b }) + +/** + * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. + * This is like `flatMap` for the exception. + */ +fun Try.recoverWith(f: (Throwable) -> Try): Try = fold({ f(it) }, { Try.Success(it) }) + +/** + * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. + * This is like map for the exception. + */ +fun Try.recover(f: (Throwable) -> B): Try = fold({ Try.Success(f(it)) }, { Try.Success(it) }) + +/** + * Completes this `Try` by applying the function `f` to this if this is of type `Failure`, + * or conversely, by applying `s` if this is a `Success`. + */ +fun Try.transform(s: (B) -> Try, f: (Throwable) -> Try): Try = fold({ f(it) }, { flatMap(s) })