Skip to content

Instantly share code, notes, and snippets.

@jimschubert
Created December 12, 2016 03:24
Show Gist options
  • Save jimschubert/965984d141f7adca149619fe2ba565bd to your computer and use it in GitHub Desktop.
Save jimschubert/965984d141f7adca149619fe2ba565bd to your computer and use it in GitHub Desktop.
Try implementation in Kotlin that I've ditched but don't want to lose
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 43ca251..9db3760 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Dec 05 21:26:08 EST 2016
+#Sun Dec 11 10:08:31 EST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.2-bin.zip
diff --git a/kopper/build.gradle b/kopper/build.gradle
index e6d71d4..7b701fe 100644
--- a/kopper/build.gradle
+++ b/kopper/build.gradle
@@ -1,11 +1,6 @@
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
- // Declare the dependency for your favourite test framework you want to use in your tests.
- // TestNG is also supported by the Gradle Test task. Just change the
- // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
- // 'test.useTestNG()' to your build script.
-
testCompile 'org.testng:testng:6.8.1',
"org.jetbrains.kotlin:kotlin-test:$kotlinVersion"
}
diff --git a/kopper/src/main/kotlin/us/jimschubert/kopper/Try.kt b/kopper/src/main/kotlin/us/jimschubert/kopper/Try.kt
new file mode 100644
index 0000000..01dfd65
--- /dev/null
+++ b/kopper/src/main/kotlin/us/jimschubert/kopper/Try.kt
@@ -0,0 +1,116 @@
+/**
+ * This code is based off of Scala's Try/Success/Failure interface.
+ * Scala is licensed to EPFL and Lightbend, Inc.
+ * see: https://github.com/scala/scala/blob/2.12.x/LICENSE
+ */
+package us.jimschubert.kopper
+
+import java.util.*
+
+interface Try<T> {
+ fun isFailure(): Boolean
+ fun isSuccess(): Boolean
+
+ fun get(): T
+
+ fun <U : T> getOrElse(default: () -> U): T
+ fun <U : T> orElse(default: () -> Try<U>): Try<U>
+
+ fun <U> foreach(f: (T) -> U): Unit
+ fun <U> flatMap(f: (T) -> Try<U>): Try<U>
+ fun <U> map(f: (T) -> U): Try<U>
+ // collect? Investigate equivalent of PartialFunction in kotlin.
+
+ fun filter(predicate: (T) -> Boolean): Try<T>
+
+ fun toOption(): T?
+
+ fun <U> fold(whenFailure: (Throwable) -> U, whenSuccess: (T) -> U) : U
+
+ // TODO: Recover
+
+ companion object {
+ operator fun <T> invoke(value: () -> T): Try<T> = try {
+ Success(value.invoke())
+ } catch (e:Exception) {
+ Failure(e)
+ }
+ }
+}
+
+data class Failure<T>(val exception: Throwable) : Try<T> {
+ override fun isFailure(): Boolean = true
+ override fun isSuccess(): Boolean = false
+ override fun get(): T {
+ throw exception
+ }
+
+ override fun <U : T> getOrElse(default: () -> U): T = default.invoke()
+
+ override fun <U : T> orElse(default: () -> Try<U>): Try<U> {
+ return try {
+ default.invoke()
+ } catch (e:Exception) {
+ return Failure(e)
+ }
+ }
+
+ override fun <U> foreach(f: (T) -> U) { }
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <U> flatMap(f: (T) -> Try<U>): Try<U> = Failure(exception)
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <U> map(f: (T) -> U): Try<U> = Failure(exception)
+
+ override fun filter(predicate: (T) -> Boolean): Try<T> = this
+
+ override fun toOption(): T? = null
+
+ override fun <U> fold(whenFailure: (Throwable) -> U, whenSuccess: (T) -> U): U = whenFailure.invoke(exception)
+}
+
+data class Success<T>(val value: T) : Try<T> {
+ override fun isFailure(): Boolean = false
+ override fun isSuccess(): Boolean = true
+ override fun get(): T = value
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <U : T> getOrElse(default: () -> U): T = get()
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <U : T> orElse(default: () -> Try<U>): Try<U> = this as Try<U>
+
+ override fun <U> foreach(f: (T) -> U) {
+ f.invoke(value)
+ }
+
+ override fun <U> flatMap(f: (T) -> Try<U>): Try<U> {
+ return try {
+ f.invoke(value)
+ } catch (e:Exception) {
+ return Failure(e)
+ }
+ }
+
+ override fun <U> map(f: (T) -> U): Try<U> = Try { f.invoke(value) }
+
+ override fun filter(predicate: (T) -> Boolean): Try<T> {
+ return try {
+ if(predicate.invoke(value)) this else Failure(NoSuchElementException("Predicate does not hold for $value"))
+ } catch (e:Exception) {
+ return Failure(e)
+ }
+ }
+
+ override fun toOption(): T? = value
+
+ override fun <U> fold(whenFailure: (Throwable) -> U, whenSuccess: (T) -> U): U {
+ return try {
+ whenSuccess(value)
+ } catch (e:Exception) {
+ return whenFailure(e)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/kopper/src/test/kotlin/us/jimschubert/kopper/TryTest.kt b/kopper/src/test/kotlin/us/jimschubert/kopper/TryTest.kt
new file mode 100644
index 0000000..ad58feb
--- /dev/null
+++ b/kopper/src/test/kotlin/us/jimschubert/kopper/TryTest.kt
@@ -0,0 +1,371 @@
+package us.jimschubert.kopper
+
+import org.testng.Assert.*
+import org.testng.annotations.Test
+import java.util.*
+
+class MyInvalidValueException : Exception()
+class TestClass(var value: Int)
+class Result(val successful: Boolean)
+
+class TryTest {
+ @Test
+ fun `Success instance for valid value`() {
+ // Arrange
+ val valid: () -> Boolean = {
+ true
+ }
+
+ // Act
+ val tried = Try(valid)
+
+ // Assert
+ assertTrue(tried is Try<Boolean>)
+ assertTrue(tried is Try<Boolean>)
+ assertTrue(tried.isSuccess())
+ assertFalse(tried.isFailure())
+ assertEquals(tried.get(), true)
+ }
+
+ @Test(expectedExceptions = arrayOf(MyInvalidValueException::class))
+ fun `Failure instance for thrown exception`() {
+ // Arrange
+ val invalid: () -> Boolean = {
+ throw MyInvalidValueException()
+ }
+
+ // Act
+ val tried = Try(invalid)
+
+ // Assert
+ assertTrue(tried is Try<Boolean>)
+ assertTrue(tried is Try<Boolean>)
+ assertFalse(tried.isSuccess())
+ assertTrue(tried.isFailure())
+
+ tried.get() // should throw
+ }
+
+ @Test
+ fun `Success getOrElse`() {
+ // Arrange
+ val value = 3
+ val success = Success(value)
+
+ // Act
+ val result = success.getOrElse { 4 }
+
+ // Assert
+ assertEquals(result, value)
+ }
+
+ @Test
+ fun `Failure getOrElse`() {
+ // Arrange
+ val value = 3
+ val failure = Failure<Int>(MyInvalidValueException())
+
+ // Act
+ val result = failure.getOrElse { value }
+
+ // Assert
+ assertEquals(result, value)
+ }
+
+ @Test
+ fun `Success orElse`() {
+ // Arrange
+ val value = 3
+ val success = Success(value)
+
+ // Act
+ val result: Try<Int> = success.orElse { Success(4) }
+
+ // Assert
+ assertTrue(result.isSuccess())
+ assertEquals(result.get(), value)
+ }
+
+ @Test
+ fun `Failure orElse`() {
+ // Arrange
+ val value = 3
+ val failure = Failure<Int>(MyInvalidValueException())
+
+ // Act
+ val result = failure.orElse { Success(value) }
+
+ // Assert
+ assertTrue(result.isSuccess())
+ assertEquals(result.get(), value)
+ }
+
+ @Test
+ fun `Success foreach`() {
+ // Arrange
+ val value = 3
+ val expected = 4
+ val accumulator = TestClass(value)
+ val success = Success(accumulator)
+
+ // Act
+ success.foreach { acc ->
+ acc.value = acc.value + 1
+ }
+
+ // Assert
+ assertTrue(success.isSuccess())
+ assertEquals(accumulator.value, expected)
+ }
+
+ @Test
+ fun `Failure foreach`() {
+ // Arrange
+ val value = 3
+ val expected = 3
+ val accumulator = TestClass(value)
+ val failure = Failure<TestClass>(MyInvalidValueException())
+
+ // Act
+ failure.foreach { acc ->
+ acc.value = acc.value + 1
+ }
+
+ // Assert
+ assertTrue(failure.isFailure())
+ assertEquals(accumulator.value, expected)
+ }
+
+ @Test
+ fun `Success flatMap to Success`() {
+ // Arrange
+ val value = 3
+ val expected = 4
+ val accumulator = TestClass(value)
+ val success = Success(accumulator)
+
+ // Act
+ val result: Try<Int> = success.flatMap { acc ->
+ Success(acc.value + 1)
+ }
+
+ // Assert
+ assertTrue(result.isSuccess())
+ assertEquals(result.get(), expected)
+ }
+
+ @Test(expectedExceptions = arrayOf(MyInvalidValueException::class))
+ fun `Success flatMap to Failure`() {
+ // Arrange
+ val value = 3
+ val accumulator = TestClass(value)
+ val success = Success(accumulator)
+
+ // Act
+ val result: Try<Int> = success.flatMap { acc ->
+ Failure<Int>(MyInvalidValueException())
+ }
+
+ // Assert
+ assertTrue(result.isFailure())
+ result.get() // should throw
+ }
+
+ @Test(expectedExceptions = arrayOf(MyInvalidValueException::class))
+ fun `Failure flatMap`() {
+ // Arrange
+ val failure = Failure<TestClass>(MyInvalidValueException())
+
+ // Act
+ val result: Try<Int> = failure.flatMap { acc ->
+ Success(acc.value + 1)
+ }
+
+ // Assert
+ assertTrue(result.isFailure())
+ result.get() // should throw
+ }
+
+ @Test
+ fun `Success map`() {
+ // Arrange
+ val value = 3
+ val expected = 4
+ val accumulator = TestClass(value)
+ val success = Success(accumulator)
+
+ // Act
+ val result: Try<Int> = success.map { acc ->
+ acc.value + 1
+ }
+
+ // Assert
+ assertTrue(result.isSuccess())
+ assertEquals(result.get(), expected)
+ }
+
+ @Test(expectedExceptions = arrayOf(MyInvalidValueException::class))
+ fun `Failure map`() {
+ // Arrange
+ val failure = Failure<TestClass>(MyInvalidValueException())
+
+ // Act
+ val result: Try<Int> = failure.map { acc ->
+ acc.value + 1
+ }
+
+ // Assert
+ assertTrue(result.isFailure())
+ result.get() // should throw
+ }
+
+ @Test
+ fun `Success filter`() {
+ // Arrange
+ val value = 3
+ val tester = TestClass(value)
+ val success = Success(tester)
+
+ // Act
+ val result: Try<TestClass> = success.filter { t ->
+ t.value == 3
+ }
+
+ // Assert
+ assertTrue(result.isSuccess())
+ assertEquals(result.get().value, value)
+ }
+
+ @Test(expectedExceptions = arrayOf(NoSuchElementException::class))
+ fun `Success filter fails`() {
+ // Arrange
+ val value = 3
+ val expected = 4
+ val tester = TestClass(value)
+ val success = Success(tester)
+
+ // Act
+ val result: Try<TestClass> = success.filter { t ->
+ t.value == 100
+ }
+
+ // Assert
+ assertTrue(result.isFailure())
+ assertEquals(result.get(), expected)
+ }
+
+ @Test(expectedExceptions = arrayOf(NotImplementedError::class))
+ fun `Success filter exception`() {
+ // Arrange
+ val value = 3
+ val expected = 4
+ val tester = TestClass(value)
+ val success = Success(tester)
+
+ // Act
+ val result: Try<TestClass> = success.filter { t ->
+ throw NotImplementedError()
+ }
+
+ // Assert
+ assertTrue(result.isFailure())
+ assertEquals(result.get(), expected)
+ }
+
+ @Test(expectedExceptions = arrayOf(MyInvalidValueException::class))
+ fun `Failure filter`() {
+ // Arrange
+ val failure = Failure<TestClass>(MyInvalidValueException())
+
+ // Act
+ val result: Try<TestClass> = failure.filter { acc ->
+ acc.value == 3
+ }
+
+ // Assert
+ assertTrue(result.isFailure())
+ result.get() // should throw
+ }
+
+ @Test
+ fun `Success toOption`() {
+ // Arrange
+ val value = 3
+ val tester = TestClass(value)
+ val success = Success(tester)
+
+ // Act
+ val result: TestClass? = success.toOption()
+
+ // Assert
+ assertEquals(result?.value, value)
+ }
+
+ @Test
+ fun `Failure toOption`() {
+ // Arrange
+ val failure = Failure<TestClass>(MyInvalidValueException())
+
+ // Act
+ val result: TestClass? = failure.toOption()
+
+ // Assert
+ assertNull(result)
+ }
+
+ @Test
+ fun `Success fold success`() {
+ // Arrange
+ val value = 3
+ val tester = TestClass(value)
+ val success = Success(tester)
+
+ // Act
+ val result: Result = success.fold({ throwable ->
+ Result(false)
+ }) { successful ->
+ Result(true)
+ }
+
+ // Assert
+ assertTrue(result.successful)
+ }
+
+ @Test
+ fun `Success fold exception`() {
+ // Arrange
+ val value = 3
+ val tester = TestClass(value)
+ val success = Success(tester)
+
+ // Act
+ val result: Result = success.fold({ throwable ->
+ Result(false)
+ }) { successful ->
+ throw MyInvalidValueException()
+ }
+
+ // Assert
+ assertFalse(result.successful)
+ }
+
+
+ @Test
+ fun `Failure fold err`() {
+ // Arrange
+ val value = 3
+ val tester = TestClass(value)
+ val success = Success(tester)
+ val failure = Failure<TestClass>(MyInvalidValueException())
+
+ // Act
+ val result: Result = (success.flatMap { failure }).fold({ throwable ->
+ Result(false)
+ }) { successful ->
+ Result(true)
+ }
+
+ // Assert
+ assertFalse(result.successful)
+ }
+}
\ No newline at end of file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment