From 0d53ce76ad23db79e2205847f8df5ce8bd7be9c3 Mon Sep 17 00:00:00 2001 From: JMPergar Date: Fri, 24 Mar 2017 12:13:22 +0100 Subject: [PATCH 1/2] Create Option type and base combinators --- .../main/java/com/finecinnamon/kats/Option.kt | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 library/src/main/java/com/finecinnamon/kats/Option.kt diff --git a/library/src/main/java/com/finecinnamon/kats/Option.kt b/library/src/main/java/com/finecinnamon/kats/Option.kt new file mode 100644 index 00000000000..125f8a7d8db --- /dev/null +++ b/library/src/main/java/com/finecinnamon/kats/Option.kt @@ -0,0 +1,159 @@ +/* + * 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 com.finecinnamon.kats + +import java.util.* + +/** + * Port of https://github.com/scala/scala/blob/v2.12.1/src/library/scala/Option.scala + * + * Represents optional values. Instances of `Option` + * are either an instance of $some or the object $none. + */ +sealed class Option { + + /** + * Returns true if the option is $none, false otherwise. + */ + abstract val isEmpty: Boolean + + /** + * Returns true if the option is an instance of $some, false otherwise. + */ + val isDefined: Boolean = !isEmpty + + /** + * Returns the option's value. + * @note The option must be nonempty. + * @throws java.util.NoSuchElementException if the option is empty. + */ + abstract fun get(): A + + /** + * Returns the option's value if the option is nonempty, otherwise + * return the result of evaluating `default`. + * + * @param default the default expression. + */ + inline fun getOrElse(default: () -> A): A = if (isEmpty) default() else get() + + /** + * Returns a $some containing the result of applying $f to this $option's + * value if this $option is nonempty. Otherwise return $none. + * + * @note This is similar to `flatMap` except here, + * $f does not need to wrap its result in an $option. + * + * @param f the function to apply + * @see flatMap + * @see foreach + */ + inline fun map(f: (A) -> B): Option = if (isEmpty) None() else Some(f(get())) + + /** + * Returns the result of applying $f to this $option's value if + * this $option is nonempty. + * Returns $none if this $option is empty. + * Slightly different from `map` in that $f is expected to + * return an $option (which could be $none). + * + * @param f the function to apply + * @see map + * @see foreach + */ + inline fun flatMap(f: (A) -> Option): Option = if (isEmpty) None() else f(get()) + + /** + * Returns the result of applying $f to this $option's + * value if the $option is nonempty. Otherwise, evaluates + * expression `ifEmpty`. + * + * @note This is equivalent to `$option map f getOrElse ifEmpty`. + * + * @param ifEmpty the expression to evaluate if empty. + * @param f the function to apply if nonempty. + */ + inline fun fold(ifEmpty: () -> B, f: (A) -> B): B = if (isEmpty) ifEmpty() else f(get()) + + /** + * Returns this $option if it is nonempty '''and''' applying the predicate $p to + * this $option's value returns true. Otherwise, return $none. + * + * @param p the predicate used for testing. + */ + inline fun filter(p: (A) -> Boolean): Option = if (isEmpty || p(get())) this else None() + + /** + * Returns this $option if it is nonempty '''and''' applying the predicate $p to + * this $option's value returns false. Otherwise, return $none. + * + * @param p the predicate used for testing. + */ + inline fun filterNot(p: (A) -> Boolean): Option = if (isEmpty || !p(get())) this else None() + + /** + * Returns false if the option is $none, true otherwise. + * @note Implemented here to avoid the implicit conversion to Iterable. + */ + val nonEmpty = isDefined + + /** + * Tests whether the option contains a given value as an element. + * + * @param elem the element to test. + * @return `true` if the option has an element that is equal (as + * determined by `==`) to `elem`, `false` otherwise. + */ + fun contains(elem: A): Boolean = !isEmpty && get() == elem + + /** + * Returns true if this option is nonempty '''and''' the predicate + * $p returns true when applied to this $option's value. + * Otherwise, returns false. + * + * @param p the predicate to test + */ + inline fun exists(p: (A) -> Boolean): Boolean = !isEmpty && p(get()) + + /** + * Returns true if this option is empty '''or''' the predicate + * $p returns true when applied to this $option's value. + * + * @param p the predicate to test + */ + inline fun forall(p: (A) -> Boolean): Boolean = exists(p) + + /** + * Apply the given procedure $f to the option's value, + * if it is nonempty. Otherwise, do nothing. + * + * @param f the procedure to apply. + * @see map + * @see flatMap + */ + inline fun foreach(f: (A) -> B) { if (!isEmpty) f(get()) } + + class Some(val value: A) : Option() { + override val isEmpty = false + override fun get(): A = value + } + + class None : Option() { + override val isEmpty = true + override fun get(): Nothing = throw NoSuchElementException("None.get") + } +} \ No newline at end of file From dc6663b2d19cdefd8bf62b24dfe7a45c55107eb1 Mon Sep 17 00:00:00 2001 From: JMPergar Date: Fri, 24 Mar 2017 12:35:37 +0100 Subject: [PATCH 2/2] Modify project structure --- {library => core}/.gitignore | 0 {library => core}/build.gradle | 4 +- core/src/main/java/kats/dummy.md | 3 + .../main/java/com/finecinnamon/kats/Option.kt | 159 ------------------ settings.gradle | 2 +- 5 files changed, 6 insertions(+), 162 deletions(-) rename {library => core}/.gitignore (100%) rename {library => core}/build.gradle (87%) create mode 100644 core/src/main/java/kats/dummy.md delete mode 100644 library/src/main/java/com/finecinnamon/kats/Option.kt diff --git a/library/.gitignore b/core/.gitignore similarity index 100% rename from library/.gitignore rename to core/.gitignore diff --git a/library/build.gradle b/core/build.gradle similarity index 87% rename from library/build.gradle rename to core/build.gradle index 988f1c45faf..a077289777a 100644 --- a/library/build.gradle +++ b/core/build.gradle @@ -26,8 +26,8 @@ repositories { apply plugin: 'maven' -group = 'finecinnamon.kats' +group = 'com.github.finecinnamon' sourceSets { - main.java.srcDirs += 'library/src/main/java' + main.java.srcDirs += 'core/src/main/java' } \ No newline at end of file diff --git a/core/src/main/java/kats/dummy.md b/core/src/main/java/kats/dummy.md new file mode 100644 index 00000000000..1c013f389b0 --- /dev/null +++ b/core/src/main/java/kats/dummy.md @@ -0,0 +1,3 @@ +Dummy file to maintain visible the package structure. + +Can be removed in future commits. diff --git a/library/src/main/java/com/finecinnamon/kats/Option.kt b/library/src/main/java/com/finecinnamon/kats/Option.kt deleted file mode 100644 index 125f8a7d8db..00000000000 --- a/library/src/main/java/com/finecinnamon/kats/Option.kt +++ /dev/null @@ -1,159 +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 com.finecinnamon.kats - -import java.util.* - -/** - * Port of https://github.com/scala/scala/blob/v2.12.1/src/library/scala/Option.scala - * - * Represents optional values. Instances of `Option` - * are either an instance of $some or the object $none. - */ -sealed class Option { - - /** - * Returns true if the option is $none, false otherwise. - */ - abstract val isEmpty: Boolean - - /** - * Returns true if the option is an instance of $some, false otherwise. - */ - val isDefined: Boolean = !isEmpty - - /** - * Returns the option's value. - * @note The option must be nonempty. - * @throws java.util.NoSuchElementException if the option is empty. - */ - abstract fun get(): A - - /** - * Returns the option's value if the option is nonempty, otherwise - * return the result of evaluating `default`. - * - * @param default the default expression. - */ - inline fun getOrElse(default: () -> A): A = if (isEmpty) default() else get() - - /** - * Returns a $some containing the result of applying $f to this $option's - * value if this $option is nonempty. Otherwise return $none. - * - * @note This is similar to `flatMap` except here, - * $f does not need to wrap its result in an $option. - * - * @param f the function to apply - * @see flatMap - * @see foreach - */ - inline fun map(f: (A) -> B): Option = if (isEmpty) None() else Some(f(get())) - - /** - * Returns the result of applying $f to this $option's value if - * this $option is nonempty. - * Returns $none if this $option is empty. - * Slightly different from `map` in that $f is expected to - * return an $option (which could be $none). - * - * @param f the function to apply - * @see map - * @see foreach - */ - inline fun flatMap(f: (A) -> Option): Option = if (isEmpty) None() else f(get()) - - /** - * Returns the result of applying $f to this $option's - * value if the $option is nonempty. Otherwise, evaluates - * expression `ifEmpty`. - * - * @note This is equivalent to `$option map f getOrElse ifEmpty`. - * - * @param ifEmpty the expression to evaluate if empty. - * @param f the function to apply if nonempty. - */ - inline fun fold(ifEmpty: () -> B, f: (A) -> B): B = if (isEmpty) ifEmpty() else f(get()) - - /** - * Returns this $option if it is nonempty '''and''' applying the predicate $p to - * this $option's value returns true. Otherwise, return $none. - * - * @param p the predicate used for testing. - */ - inline fun filter(p: (A) -> Boolean): Option = if (isEmpty || p(get())) this else None() - - /** - * Returns this $option if it is nonempty '''and''' applying the predicate $p to - * this $option's value returns false. Otherwise, return $none. - * - * @param p the predicate used for testing. - */ - inline fun filterNot(p: (A) -> Boolean): Option = if (isEmpty || !p(get())) this else None() - - /** - * Returns false if the option is $none, true otherwise. - * @note Implemented here to avoid the implicit conversion to Iterable. - */ - val nonEmpty = isDefined - - /** - * Tests whether the option contains a given value as an element. - * - * @param elem the element to test. - * @return `true` if the option has an element that is equal (as - * determined by `==`) to `elem`, `false` otherwise. - */ - fun contains(elem: A): Boolean = !isEmpty && get() == elem - - /** - * Returns true if this option is nonempty '''and''' the predicate - * $p returns true when applied to this $option's value. - * Otherwise, returns false. - * - * @param p the predicate to test - */ - inline fun exists(p: (A) -> Boolean): Boolean = !isEmpty && p(get()) - - /** - * Returns true if this option is empty '''or''' the predicate - * $p returns true when applied to this $option's value. - * - * @param p the predicate to test - */ - inline fun forall(p: (A) -> Boolean): Boolean = exists(p) - - /** - * Apply the given procedure $f to the option's value, - * if it is nonempty. Otherwise, do nothing. - * - * @param f the procedure to apply. - * @see map - * @see flatMap - */ - inline fun foreach(f: (A) -> B) { if (!isEmpty) f(get()) } - - class Some(val value: A) : Option() { - override val isEmpty = false - override fun get(): A = value - } - - class None : Option() { - override val isEmpty = true - override fun get(): Nothing = throw NoSuchElementException("None.get") - } -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 54d305f4a6b..2620d053ced 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':futurek' +include ':core'