├── .gitignore ├── .travis.yml ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src ├── main └── kotlin │ └── kotlinslang │ ├── Value.kt │ ├── ValueExt.kt │ ├── algebra │ ├── Functor.kt │ ├── Initializer.kt │ ├── Monad.kt │ └── Monoid.kt │ ├── collection │ ├── CollectionExt.kt │ └── Initializer.kt │ ├── control │ ├── Either.kt │ ├── EitherExt.kt │ ├── Failure.kt │ ├── Initializer.kt │ ├── Left.kt │ ├── None.kt │ ├── Option.kt │ ├── Right.kt │ ├── Some.kt │ ├── Success.kt │ ├── Try.kt │ └── TryExt.kt │ └── function │ ├── Currying.kt │ ├── FunctionExt.kt │ └── Reverse.kt └── test └── kotlin └── kotlinslang ├── UtilTest.kt ├── ValueTest.kt ├── algebra └── AlgebraTest.kt ├── control ├── OptionTest.kt ├── TryTest.kt └── UtilTest.kt └── function └── FunctionExtTest.kt /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | build 3 | deploy 4 | .idea 5 | .gradle 6 | *.iml 7 | 8 | logs 9 | *.log 10 | 11 | /commons/build 12 | /api-endpoint/build 13 | /grader-worker/build 14 | /.idea/workspace.xml 15 | /.idea/task.xml 16 | 17 | # Eclipse Setting 18 | /.metadata 19 | /Servers 20 | .settings 21 | .project 22 | .classpath 23 | .buildpath 24 | 25 | # Maven 26 | target 27 | 28 | # Netbeans Ignore 29 | nbproject 30 | dist 31 | private 32 | 33 | #idea project 34 | *.iws 35 | *.ipr 36 | 37 | # OS generated files # 38 | .DS_Store 39 | .DS_Store? 40 | ._* 41 | .Spotlight-V100 42 | .Trashes 43 | ehthumbs.db 44 | Thumbs.db 45 | 46 | #Project Spesific 47 | 48 | !config/libs/*.jar 49 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | sudo: false 3 | jdk: 4 | - oraclejdk8 5 | addons: 6 | apt: 7 | packages: 8 | - oracle-java8-installer 9 | before_install: 10 | - pip install --user codecov 11 | after_success: 12 | - codecov -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kotlinslang 2 | A functional library for Kotlin that provides functional control structures. Inspired by Javaslang and funKTionale. 3 | 4 | [![Build Status](https://travis-ci.org/kotlinslang/kotlinslang.svg?branch=master)](https://travis-ci.org/kotlinslang/kotlinslang) 5 | [![codecov.io](https://codecov.io/github/kotlinslang/kotlinslang/coverage.svg?branch=master)](https://codecov.io/github/kotlinslang/kotlinslang?branch=master) 6 | 7 | ## Introduction 8 | Kotlinslang is a functional library for kotlin that provides functional (Monadic) control structures such as `Try`, `Option` and `Either` also helpers for functional programming. 9 | Heavily inspired by [Javaslang](javaslang.com) and [funKTonale](https://github.com/MarioAriasC/funKTionale). 10 | 11 | ## Usage 12 | For now Kotlinslang library available at bintray.com. 13 | 14 | ### Gradle 15 | You must configure your `gradle.build` to use specific maven repository from bintray. 16 | ``` 17 | repositories { 18 | mavenCentral() 19 | maven { url "http://dl.bintray.com/jasoet-gdp/org.kotlinslang" } 20 | } 21 | ``` 22 | 23 | Then you can use Kotlinslang dependency. 24 | ``` 25 | dependencies { 26 | compile "org.kotlinslang:kotlinslang:1.0.2" 27 | } 28 | ``` 29 | 30 | ### Maven 31 | You must configure your `pom.xml` to use specific maven repository from bintray. 32 | ```xml 33 | 34 | bintray 35 | bintray 36 | http://dl.bintray.com/jasoet-gdp/org.kotlinslang 37 | 38 | ``` 39 | 40 | Then add the dependency 41 | ```xml 42 | 43 | org.kotlinslang 44 | kotlinslang 45 | 1.0.2 46 | 47 | ``` 48 | 49 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | maven { url 'http://oss.sonatype.org/content/repositories/snapshots' } 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinGradleVersion}" 9 | classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:${bintrayGradleVersion}" 10 | } 11 | } 12 | 13 | apply plugin: 'kotlin' 14 | apply plugin: "jacoco" 15 | apply plugin: 'com.jfrog.bintray' 16 | 17 | repositories { 18 | mavenCentral() 19 | maven { url 'http://oss.sonatype.org/content/repositories/snapshots' } 20 | } 21 | 22 | jacoco { 23 | toolVersion = "${jacocoToolVersion}" 24 | } 25 | 26 | jacocoTestReport { 27 | reports { 28 | xml.enabled true 29 | csv.enabled false 30 | html.destination "${buildDir}/reports/coverage" 31 | } 32 | } 33 | 34 | check.dependsOn jacocoTestReport 35 | 36 | dependencies { 37 | compile "org.jetbrains.kotlin:kotlin-jdk-annotations:${kotlinJdkAnnotationVersion}" 38 | compile "org.jetbrains.kotlin:kotlin-stdlib:${kotlinGradleVersion}" 39 | testCompile "org.jetbrains.kotlin:kotlin-test:${kotlinGradleVersion}" 40 | testCompile("org.assertj:assertj-core:${assertJVersion}") 41 | testCompile("junit:junit:${junitVersion}") 42 | } 43 | 44 | // custom tasks for creating source/javadoc jars 45 | task sourcesJar(type: Jar, dependsOn: classes) { 46 | classifier = 'sources' 47 | from sourceSets.main.allSource 48 | } 49 | 50 | // add javadoc/source jar tasks as artifacts 51 | artifacts { 52 | archives sourcesJar 53 | } 54 | 55 | 56 | bintray { 57 | user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') 58 | key = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') 59 | configurations = ['archives'] 60 | publish = true 61 | pkg { 62 | repo = 'org.kotlinslang' 63 | name = 'kotlinslang' 64 | desc = "A functional library for Kotlin that provides functional control structures." 65 | userOrg = user 66 | licenses = ['Apache-2.0'] 67 | vcsUrl = 'https://github.com/jasoet-gdp/kotlinslang.git' 68 | issueTrackerUrl = 'https://github.com/jasoet-gdp/kotlinslang/issues' 69 | labels = ['kotlin', 'functional', 'control'] 70 | publicDownloadNumbers = true 71 | version { 72 | name = "${project.version}" 73 | desc = "Kotlinslang version ${project.version}, Need More Test!." 74 | vcsTag = "${project.version}" 75 | } 76 | } 77 | } 78 | task wrapper(type: Wrapper) { 79 | gradleVersion = "2.13" 80 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version = 1.0.2 2 | group = org.kotlinslang 3 | name = kotlinslang 4 | 5 | assertJVersion = 3.2.0 6 | kotlinGradleVersion = 1.0.2 7 | kotlinJdkAnnotationVersion = 1.0.0 8 | junitVersion = 4.12 9 | jacocoToolVersion = 0.7.5.201505241946 10 | bintrayGradleVersion = 1.5 11 | 12 | bintrayUser = jasoet-gdp 13 | bintrayApiKey = de67801ce6f1ffbfe3f24c94cfb38070f8af6aeb -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kotlins/kotlinslang/a69c7ec127eee00ff994b67662e2c1c3fbc90a55/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Feb 16 08:40:11 WIB 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >&- 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >&- 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/Value.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang 2 | 3 | import kotlinslang.algebra.Monad 4 | 5 | 6 | /** 7 | * Functional programming is all about values and transformation of values using functions. The {@code Value} 8 | * type reflects the values in a functional setting. It can be seen as the result of a partial function application. 9 | * Hence the result may be undefined. If a value is undefined, we say it is empty. 10 | *

11 | * How the empty state is interpreted depends on the context, i.e. it may be undefined, failed, 12 | * not yet defined, etc. 13 | *

14 | * 15 | * Static methods: 16 | * 17 | *

    18 | *
  • {@link #get(java.lang.Iterable)}
  • 19 | *
20 | * 21 | * Basic operations: 22 | * 23 | *
    24 | *
  • {@link #get()}
  • 25 | *
  • {@link #getOption()}
  • 26 | *
  • {@link #ifDefined(Supplier, Supplier)}
  • 27 | *
  • {@link #ifDefined(Object, Object)}
  • 28 | *
  • {@link #ifEmpty(Supplier, Supplier)}
  • 29 | *
  • {@link #ifEmpty(Object, Object)}
  • 30 | *
  • {@link #isDefined()}
  • 31 | *
  • {@link #isEmpty()}
  • 32 | *
  • {@link #orElse(Object)}
  • 33 | *
  • {@link #orElseGet(Supplier)}
  • 34 | *
  • {@link #orElseThrow(Supplier)}
  • 35 | *
36 | * 37 | * Equality checks: 38 | * 39 | *
    40 | *
  • {@link #corresponds(java.lang.Iterable, BiPredicate)}
  • 41 | *
  • {@link #eq(Object)}
  • 42 | *
43 | * 44 | * Side-effects: 45 | * 46 | *
    47 | *
  • {@link #out(PrintStream)}
  • 48 | *
  • {@link #out(PrintWriter)}
  • 49 | *
  • {@link #peek(Consumer)}
  • 50 | *
  • {@link #stderr()}
  • 51 | *
  • {@link #stdout()}
  • 52 | *
53 | * 54 | * Tests: 55 | * 56 | *
    57 | *
  • {@link #isSingletonType()}
  • 58 | *
59 | * 60 | * @param The type of the wrapped value. 61 | * @author Daniel Dietrich, Deny Prasetyo 62 | * @since 1.0.0 63 | */ 64 | 65 | interface Value : Iterable, Monad { 66 | 67 | companion object { 68 | /** 69 | * Gets the first value of the given Iterable if exists, otherwise throws. 70 | * 71 | * @param iterable An java.lang.Iterable 72 | * @param Component type 73 | * @return An object of type T 74 | * @throws java.util.NoSuchElementException if the given iterable is empty 75 | */ 76 | operator fun get(iterable: Iterable): T { 77 | if (iterable is Value) { 78 | return iterable.get() 79 | } else { 80 | return iterable.iterator().next() 81 | } 82 | } 83 | } 84 | 85 | fun get(): T 86 | 87 | /** 88 | * Checks, this {@code Value} is empty, i.e. if the underlying value is absent. 89 | * 90 | * @return false, if no underlying value is present, true otherwise. 91 | */ 92 | fun isEmpty(): Boolean 93 | 94 | /** 95 | * Checks, this {@code Value} is defined, i.e. if the underlying value is present. 96 | * 97 | * @return true, if an underlying value is present, false otherwise. 98 | */ 99 | fun isDefined(): Boolean { 100 | return !isEmpty() 101 | } 102 | 103 | fun orNull(): T? { 104 | return if (isEmpty()) null else get() 105 | } 106 | 107 | 108 | /** 109 | * Returns the underlying value if present, otherwise throws {@code supplier.get()}. 110 | * 111 | * @param a Throwable type 112 | * @param supplier An exception supplier. 113 | * @return A value of type {@code T}. 114 | * @throws NullPointerException if supplier is null 115 | * @throws X if no value is present 116 | */ 117 | @Throws(exceptionClasses = Throwable::class) 118 | fun orElseThrow(supplier: () -> X): T { 119 | if (isEmpty()) { 120 | throw supplier() 121 | } else { 122 | return get() 123 | } 124 | } 125 | 126 | /** 127 | * Performs the given {@code action} on the first element if this is an eager implementation. 128 | * Performs the given {@code action} on all elements (the first immediately, successive deferred), 129 | * if this is a lazy implementation. 130 | * 131 | * @param action The action that will be performed on the element(s). 132 | * @return this instance 133 | */ 134 | fun peek(action: (T) -> Unit): Value 135 | 136 | /** 137 | * Clarifies that values have a proper equals() method implemented. 138 | *

139 | * See Object.equals(Object). 140 | * 141 | * @param o An object 142 | * @return true, if this equals o, false otherwise 143 | */ 144 | override fun equals(other: Any?): Boolean 145 | 146 | /** 147 | * Clarifies that values have a proper hashCode() method implemented. 148 | *

149 | * See Object.hashCode(). 150 | * 151 | * @return The hashcode of this object 152 | */ 153 | override fun hashCode(): Int 154 | 155 | /** 156 | * Clarifies that values have a proper toString() method implemented. 157 | *

158 | * See Object.toString(). 159 | * 160 | * @return A String representation of this object 161 | */ 162 | override fun toString(): String 163 | 164 | 165 | override fun forEach(operation: (T) -> Unit) { 166 | for (item: T in this) { 167 | operation(item) 168 | } 169 | } 170 | 171 | override fun any(predicate: (T) -> Boolean): Boolean { 172 | for (t in this) { 173 | if (predicate(t)) { 174 | return true 175 | } 176 | } 177 | return false 178 | } 179 | 180 | override fun all(predicate: (T) -> Boolean): Boolean { 181 | for (t in this) { 182 | if (!predicate(t)) { 183 | return false 184 | } 185 | } 186 | 187 | return true; 188 | } 189 | 190 | override fun filter(predicate: (T) -> Boolean): Value 191 | override fun flatMap(mapper: (T) -> Iterable): Value 192 | override fun map(mapper: (T) -> U?): Value 193 | } 194 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/ValueExt.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang 2 | 3 | import kotlinslang.algebra.Monoid 4 | import kotlinslang.control.None 5 | import kotlinslang.control.Option 6 | import kotlinslang.control.Some 7 | import kotlinslang.control.Try 8 | import kotlinslang.control.tryOf 9 | 10 | 11 | /** 12 | * Extension functions for {@code Value} 13 | * 14 | * @author Deny Prasetyo. 15 | * @since 1.0.0 16 | */ 17 | 18 | 19 | /** 20 | * [Todo Documentation] 21 | */ 22 | fun Value.toOption(): Option { 23 | if (this is Option) { 24 | return this 25 | } else { 26 | return if (isEmpty()) None else Some(get()) 27 | } 28 | } 29 | 30 | /** 31 | * [Todo Documentation] 32 | */ 33 | fun Value.toTry(): Try { 34 | if (this is Try) { 35 | return this 36 | } else { 37 | return tryOf({ this.get() }) 38 | } 39 | } 40 | 41 | 42 | /** 43 | * Returns the underlying value if present, otherwise {@code other}. 44 | * 45 | * @param other An alternative value. 46 | * @return A value of type {@code T} 47 | */ 48 | fun Value.orElse(other: T): T { 49 | return if (isEmpty()) other else get() 50 | } 51 | 52 | /** 53 | * Returns the underlying value if present, otherwise {@code other}. 54 | * 55 | * @param supplier An alternative value supplier. 56 | * @return A value of type {@code T} 57 | */ 58 | fun Value.orElseGet(supplier: () -> T): T { 59 | return if (isEmpty()) supplier() else get() 60 | } 61 | 62 | 63 | /** 64 | * [Todo Documentation] 65 | */ 66 | fun Value.foldLeft(zero: U, combiner: (U, T) -> U): U { 67 | return if (isEmpty()) zero else combiner(zero, get()) 68 | } 69 | 70 | 71 | /** 72 | * [Todo Documentation] 73 | */ 74 | fun Value.foldRight(zero: U, combiner: (T, U) -> U): U { 75 | return if (isEmpty()) zero else combiner(get(), zero) 76 | } 77 | 78 | 79 | /** 80 | * A fluent if-expression for this value. If this is defined (i.e. not empty) trueVal is returned, 81 | * otherwise falseVal is returned. 82 | * 83 | * @param trueVal The result, if this is defined. 84 | * @param falseVal The result, if this is not defined. 85 | * @return trueVal if this.isDefined(), otherwise falseVal. 86 | */ 87 | fun Value.ifDefined(trueVal: T, falseVal: T): T { 88 | return if (isDefined()) trueVal else falseVal 89 | } 90 | 91 | /** 92 | * A fluent if-expression for this value. If this is defined (i.e. not empty) trueSupplier() is returned, 93 | * otherwise falseSupplier() is returned. 94 | * 95 | * @param trueSupplier The result, if this is defined. 96 | * @param falseSupplier The result, if this is not defined. 97 | * @return trueSupplier() if this.isDefined(), otherwise falseSupplier(). 98 | */ 99 | fun Value.ifDefined(trueSupplier: () -> T, falseSupplier: () -> T): T { 100 | return if (isDefined()) trueSupplier() else falseSupplier() 101 | } 102 | 103 | /** 104 | * A fluent if-expression for this value. If this is empty (i.e. not defined) trueVal is returned, 105 | * otherwise falseVal is returned. 106 | * 107 | * @param trueVal The result, if this is empty. 108 | * @param falseVal The result, if this is not empty. 109 | * @return trueVal if this.isEmpty(), otherwise falseVal. 110 | */ 111 | fun Value.ifEmpty(trueVal: T, falseVal: T): T { 112 | return if (isEmpty()) trueVal else falseVal 113 | } 114 | 115 | /** 116 | * A fluent if-expression for this value. If this is empty (i.e. not defined) trueSupplier() is returned, 117 | * otherwise falseSupplier() is returned. 118 | * 119 | * @param trueSupplier The result, if this is defined. 120 | * @param falseSupplier The result, if this is not defined. 121 | * @return trueSupplier() if this.isEmpty(), otherwise falseSupplier(). 122 | */ 123 | fun Value.ifEmpty(trueSupplier: () -> T, falseSupplier: () -> T): T { 124 | return if (isEmpty()) trueSupplier() else falseSupplier() 125 | } 126 | 127 | 128 | /** 129 | * Folds this elements from the left, starting with {@code monoid.zero()} and successively calling {@code monoid::combine}. 130 | * 131 | * @param monoid A monoid, providing a {@code zero} and a {@code combine} function. 132 | * @return a folded value 133 | */ 134 | fun Value.fold(monoid: Monoid): T { 135 | return foldLeft(monoid) 136 | } 137 | 138 | /** 139 | * Folds this elements from the left, starting with {@code zero} and successively calling {@code combine}. 140 | * 141 | * @param zero A zero element to start with. 142 | * @param combiner A function which combines elements. 143 | * @return a folded value 144 | */ 145 | fun Value.fold(zero: T, combiner: (T, T) -> T): T { 146 | return foldLeft(zero, combiner) 147 | } 148 | 149 | /** 150 | * Folds this elements from the left, starting with {@code monoid.zero()} and successively calling {@code monoid::combine}. 151 | * 152 | * @param monoid A monoid, providing a {@code zero} and a {@code combine} function. 153 | * @return a folded value 154 | */ 155 | fun Value.foldLeft(monoid: Monoid): T { 156 | return foldLeft(monoid.zero(), { t, t2 -> monoid.combine(t, t2) }) 157 | } 158 | 159 | /** 160 | * Maps this elements to a {@code Monoid} and applies {@code foldLeft}, starting with {@code monoid.zero()}: 161 | *


162 |  *  foldLeft(monoid.zero(), (ys, x) -> monoid.combine(ys, mapper(x)));
163 |  * 
164 | * 165 | * @param monoid A Monoid 166 | * @param mapper A mapper 167 | * @param Component type of the given monoid. 168 | * @return the folded monoid value. 169 | */ 170 | fun Value.foldMap(monoid: Monoid, mapper: (T) -> U): U { 171 | return foldLeftMap(monoid, mapper) 172 | } 173 | 174 | /** 175 | * Maps this elements to a {@code Monoid} and applies {@code foldLeft}, starting with {@code monoid.zero()}: 176 | *

177 |  *  foldLeft(monoid.zero(), (ys, x) -> monoid.combine(ys, mapper(x)));
178 |  * 
179 | * 180 | * @param monoid A Monoid 181 | * @param mapper A mapper 182 | * @param Component type of the given monoid. 183 | * @return the folded monoid value. 184 | */ 185 | fun Value.foldLeftMap(monoid: Monoid, mapper: (T) -> U): U { 186 | return foldLeft(monoid.zero(), { ys, x -> monoid.combine(ys, mapper(x)) }) 187 | } 188 | 189 | /** 190 | * Maps this elements to a {@code Monoid} and applies {@code foldLeft}, starting with {@code monoid.zero()}: 191 | *

192 |  *  foldLeft(monoid.zero(), (ys, x) -> monoid.combine(ys, mapper(x)));
193 |  * 
194 | * 195 | * @param monoid A Monoid 196 | * @param mapper A mapper 197 | * @param Component type of the given monoid. 198 | * @return the folded monoid value. 199 | */ 200 | fun Value.foldRightMap(monoid: Monoid, mapper: (T) -> U): U { 201 | return foldRight(monoid.zero(), { ys, x -> monoid.combine(mapper(ys), x) }) 202 | } 203 | 204 | /** 205 | * Folds this elements from the right, starting with {@code monoid.zero()} and successively calling {@code monoid::combine}. 206 | * 207 | * @param monoid A monoid, providing a {@code zero} and a {@code combine} function. 208 | * @return a folded value 209 | */ 210 | fun Value.foldRight(monoid: Monoid): T { 211 | return foldRight(monoid.zero(), { t, t2 -> monoid.combine(t, t2) }) 212 | } 213 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/algebra/Functor.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.algebra 2 | 3 | 4 | /** 5 | *

Defines a Functor by generalizing the map.

6 | * 7 | * All instances of the Functor interface should obey the two functor laws: 8 | *
    9 | *
  • {@code m.map(a -> a) ≡ m}
  • 10 | *
  • {@code m.map(f.compose(g)) ≡ m.map(g).map(f)}
  • 11 | *
12 | * where "f, g ∈ Function". 13 | * 14 | * @param component type of this functor 15 | * @author Daniel Dietrich, Deny Prasetyo. 16 | * @see The functor design pattern 17 | * @since 1.0.0 18 | */ 19 | 20 | interface Functor { 21 | 22 | /** 23 | * Applies a function f to the components of this Functor. 24 | * 25 | * @param type of the component of this functor 26 | * @param type of the component of the resulting Functor 27 | * @param mapper a Function which maps the component of this Functor 28 | * @return a new Functor 29 | */ 30 | fun map(mapper: (T) -> (U?)): Functor 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/algebra/Initializer.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.algebra 2 | 3 | import kotlinslang.function.compose 4 | import kotlinslang.function.identity 5 | 6 | 7 | /** 8 | * Helper function for algebra 9 | * 10 | * @author Deny Prasetyo. 11 | * @since 1.0.0 12 | */ 13 | 14 | /** 15 | * Lifts a {@code Function} to a higher {@code Function} that operates on Monads. 16 | * 17 | * @param 1st argument type of f 18 | * @param result type of f 19 | * @param function a Function 20 | * @return a new Function that lifts the given function f in a layer that operates on monads. 21 | */ 22 | fun monadLift(function: (T) -> (R)): (Monad) -> Monad { 23 | return { mt: Monad -> mt.map { function(it) } } 24 | } 25 | 26 | /** 27 | * Factory method for monoids, taking a zero and a combiner Function. 28 | * 29 | * @param Value type 30 | * @param zero The zero of the Monoid. 31 | * @param combiner The associative binary operation of the Monoid. 32 | * @return a new Monoid on type A 33 | */ 34 | fun monoidOf(zero: A, combiner: (A, A) -> A): Monoid { 35 | return object : Monoid { 36 | override fun zero(): A { 37 | return zero 38 | } 39 | 40 | override fun combine(a1: A, a2: A): A { 41 | return combiner(a1, a2) 42 | } 43 | } 44 | } 45 | 46 | /** 47 | * The monoid of endomorphisms under composition. 48 | * 49 | * @param Value type 50 | * @return The monoid of endomorphisms of type A. 51 | */ 52 | fun endoMonoid(): Monoid<(A) -> A> { 53 | return monoidOf(identity(), { a, b -> a.compose(b) }) 54 | } -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/algebra/Monad.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.algebra 2 | 3 | 4 | /** 5 | * Defines a Monad by generalizing the flatMap function. 6 | *

7 | * A {@code Monad} is a {@link Functor} with a {@code flatMap} method that satisfies the Monad laws, also known 8 | * as the three control laws: 9 | *

10 | * Let 11 | *

18 | * Then all instances of the {@code Monad} interface should obey the three control laws: 19 | *
    20 | *
  • Left identity: {@code unit(a).flatMap(f) ≡ f a}
  • 21 | *
  • Right identity: {@code m.flatMap(unit) ≡ m}
  • 22 | *
  • Associativity: {@code m.flatMap(f).flatMap(g) ≡ m.flatMap(x -> f(x).flatMap(g))}
  • 23 | *
24 | * 25 | * To read further about monads in Java please refer to 26 | *
What's Wrong in Java 8, Part IV: Monads. 27 | * 28 | * @param component type of this monad 29 | * @author Daniel Dietrich, Deny Prasetyo 30 | * @since 1.0.0 31 | */ 32 | 33 | interface Monad : Functor, Iterable { 34 | 35 | /** 36 | * Filters this `Monad` by testing a predicate. 37 | * 38 | * If given filtered.isEmpty() will return true, if no element satisfied 39 | * the given predicate. 40 | * 41 | * 42 | * Also, an implementation may throw `NoSuchElementException`, if no element makes it through the filter 43 | * and this state cannot be reflected. 44 | * 45 | * @param predicate A predicate function 46 | * @return a new Monad instance 47 | */ 48 | fun filter(predicate: (T) -> Boolean): Monad 49 | 50 | 51 | /** 52 | * FlatMaps this value to a new value with different component type. 53 | *

54 | * FlatMap is the sequence operation for functions and behaves like the imperative {@code ;}. 55 | *

56 | * If the previous results are needed, flatMap cascades: 57 | *

 58 |      * 
 59 |      * m1().flatMap(result1 ->
 60 |      *      m2(result1).flatMap(result2 ->
 61 |      *          m3(result1, result2).flatMap(result3 ->
 62 |      *              ...
 63 |      *          )
 64 |      *      )
 65 |      * );
 66 |      * 
 67 |      * 
68 | * If only the last result is needed, flatMap may be used sequentially: 69 | *
 70 |      * 
 71 |      * m1().flatMap(this::m2)
 72 |      *     .flatMap(this::m3)
 73 |      *     .flatMap(...);
 74 |      * 
 75 |      * 
76 | * 77 | * @param mapper A mapper 78 | * @param Component type of the mapped {@code Monad} 79 | * @return a mapped {@code Monad} 80 | */ 81 | fun flatMap(mapper: (T) -> Iterable): Monad 82 | 83 | /** 84 | * Performs the given [operation] on element. 85 | * Only invoked when {@code Monad} is not Empty. 86 | */ 87 | fun forEach(operation: (T) -> Unit) 88 | 89 | /** 90 | * Checks, if an element exists such that the predicate holds. 91 | * 92 | * @param predicate A Predicate function 93 | * @return true, if predicate holds for one or more elements, false otherwise 94 | */ 95 | fun any(predicate: (T) -> Boolean): Boolean 96 | 97 | /** 98 | * Checks, if the given predicate holds for all elements. 99 | * 100 | * @param predicate A Predicate 101 | * @return true, if the predicate holds for all elements, false otherwise 102 | */ 103 | fun all(predicate: (T) -> Boolean): Boolean 104 | 105 | /** 106 | * Maps this value to a new value with different component type. 107 | * 108 | * @param mapper A mapper 109 | * @param Component type of the mapped {@code Monad} 110 | * @return a mapped {@code Monad} 111 | */ 112 | override fun map(mapper: (T) -> U?): Monad 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/algebra/Monoid.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.algebra 2 | 3 | /** 4 | *

A Monoid is a types with an associative binary operation that has an 5 | * identity element {@code zero}.

6 | *

Given a type {@code A}, instances of Monoid should satisfy the following laws:

7 | *
    8 | *
  • Associativity: {@code combine(combine(x,y),z) == combine(x,combine(y,z))} for any {@code x,y,z} of type 9 | * {@code A}.
  • 10 | *
  • Identity: {@code combine(zero(), x) == x == combine(x, zero())} for any {@code x} of type {@code A}.
  • 11 | *
12 | *

Example: {@linkplain kotlin.String} is a Monoid with zero {@code ""} (empty String) and String concatenation 13 | * {@code +} as combine operation.

14 | *

Please note that some types can be viewed as a monoid in more than one way, e.g. both addition and multiplication 15 | * on numbers.

16 | * 17 | * @param A type. 18 | * @author Daniel Dietrich, Deny Prasetyo. 19 | * @since 1.0.0 20 | */ 21 | 22 | interface Monoid { 23 | 24 | /** 25 | * The unique neutral element regarding {@linkplain #combine(A, A)}. 26 | * 27 | * @return The zero element of this Monoid 28 | */ 29 | fun zero(): A 30 | 31 | /** 32 | * Combines two elements of the same type, which is also returned. 33 | * 34 | * @param a1 An element 35 | * @param a2 Another element 36 | * @return The combination of a1 and a2 37 | */ 38 | fun combine(a1: A, a2: A): A 39 | } -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/collection/CollectionExt.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.collection 2 | 3 | import kotlinslang.control.Option 4 | import kotlinslang.control.Some 5 | import kotlinslang.control.toOption 6 | 7 | 8 | /** 9 | * Extensions for Kotlin Collection that related to Kotlinslang Construct. 10 | * 11 | * @author Deny Prasetyo. 12 | * @since 1.0.0 13 | */ 14 | 15 | 16 | fun List.head(): T { 17 | return this.first() 18 | } 19 | 20 | fun List.tail(): List { 21 | return this.drop(1) 22 | } 23 | 24 | infix fun T.prependTo(list: List): List { 25 | return listOf(this) + list 26 | } 27 | 28 | fun Iterable.head(): T { 29 | return this.first() 30 | } 31 | 32 | fun Iterable.firstOption(): Option { 33 | return firstOrNull().toOption() 34 | } 35 | 36 | fun List.firstOption(): Option { 37 | return firstOrNull().toOption() 38 | } 39 | 40 | inline fun Iterable.firstOption(predicate: (T) -> Boolean): Option { 41 | return firstOrNull(predicate).toOption() 42 | } 43 | 44 | inline fun Sequence.firstOption(predicate: (T) -> Boolean): Option { 45 | return firstOrNull(predicate).toOption() 46 | } 47 | 48 | fun List.traverse(f: (T) -> Option): Option> { 49 | return foldRight(Some(emptyList())) { i: T, accumulator: Option> -> 50 | f(i).map(accumulator) { head: R, tail: List -> 51 | head prependTo tail 52 | } 53 | } 54 | } 55 | 56 | fun List>.sequential(): Option> { 57 | return traverse { it } 58 | } 59 | 60 | fun List>.flatten(): List { 61 | return filter { it.isDefined() }.map { it.get() } 62 | } 63 | 64 | 65 | fun Sequence.firstOption(): Option { 66 | return firstOrNull().toOption() 67 | } 68 | 69 | 70 | fun Array.firstOption(): Option { 71 | return firstOrNull().toOption() 72 | } 73 | 74 | fun BooleanArray.firstOption(): Option { 75 | return firstOrNull().toOption() 76 | } 77 | 78 | fun ByteArray.firstOption(): Option { 79 | return firstOrNull().toOption() 80 | } 81 | 82 | fun CharArray.firstOption(): Option { 83 | return firstOrNull().toOption() 84 | } 85 | 86 | fun DoubleArray.firstOption(): Option { 87 | return firstOrNull().toOption() 88 | } 89 | 90 | fun FloatArray.firstOption(): Option { 91 | return firstOrNull().toOption() 92 | } 93 | 94 | 95 | fun IntArray.firstOption(): Option { 96 | return firstOrNull().toOption() 97 | } 98 | 99 | 100 | fun LongArray.firstOption(): Option { 101 | return firstOrNull().toOption() 102 | } 103 | 104 | 105 | fun ShortArray.firstOption(): Option { 106 | return firstOrNull().toOption() 107 | } 108 | 109 | 110 | fun String.firstOption(): Option { 111 | return firstOrNull().toOption() 112 | } 113 | 114 | inline fun Array.firstOption(predicate: (T) -> Boolean): Option { 115 | return firstOrNull(predicate).toOption() 116 | } 117 | 118 | inline fun BooleanArray.firstOption(predicate: (Boolean) -> Boolean): Option { 119 | return firstOrNull(predicate).toOption() 120 | } 121 | 122 | inline fun ByteArray.firstOption(predicate: (Byte) -> Boolean): Option { 123 | return firstOrNull(predicate).toOption() 124 | } 125 | 126 | inline fun CharArray.firstOption(predicate: (Char) -> Boolean): Option { 127 | return firstOrNull(predicate).toOption() 128 | } 129 | 130 | inline fun DoubleArray.firstOption(predicate: (Double) -> Boolean): Option { 131 | return firstOrNull(predicate).toOption() 132 | } 133 | 134 | inline fun FloatArray.firstOption(predicate: (Float) -> Boolean): Option { 135 | return firstOrNull(predicate).toOption() 136 | } 137 | 138 | inline fun IntArray.firstOption(predicate: (Int) -> Boolean): Option { 139 | return firstOrNull(predicate).toOption() 140 | } 141 | 142 | inline fun LongArray.firstOption(predicate: (Long) -> Boolean): Option { 143 | return firstOrNull(predicate).toOption() 144 | } 145 | 146 | inline fun ShortArray.firstOption(predicate: (Short) -> Boolean): Option { 147 | return firstOrNull(predicate).toOption() 148 | } 149 | 150 | 151 | inline fun String.firstOption(predicate: (Char) -> Boolean): Option { 152 | return firstOrNull(predicate).toOption() 153 | } 154 | 155 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/collection/Initializer.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.collection 2 | 3 | /** 4 | * Helper Functions to Initialize Collection. Used by Kotlinslang. 5 | * 6 | * @author Deny Prasetyo. 7 | * @since 1.0.0 8 | */ 9 | 10 | 11 | private object EMPTY : AbstractIterator() { 12 | override fun computeNext() { 13 | done() 14 | } 15 | } 16 | 17 | fun emptyIterator(): Iterator { 18 | return EMPTY 19 | } 20 | 21 | fun iteratorOf(element: T): Iterator { 22 | return listOf(element).iterator() 23 | } 24 | 25 | fun iteratorOf(vararg element: T): Iterator { 26 | return listOf(*element).iterator() 27 | } 28 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/control/Either.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import kotlinslang.Value 4 | import kotlinslang.collection.emptyIterator 5 | import kotlinslang.collection.iteratorOf 6 | import java.util.NoSuchElementException 7 | import java.util.Objects 8 | 9 | 10 | /** 11 | * Either represents a value of two possible types. An Either is either a {@link javaslang.control.Left} or a 12 | * {@link javaslang.control.Right}. 13 | *

14 | * If the given Either is a Right and projected to a Left, the Left operations have no effect on the Right value.
15 | * If the given Either is a Left and projected to a Right, the Right operations have no effect on the Left value.
16 | * If a Left is projected to a Left or a Right is projected to a Right, the operations have an effect. 17 | *

18 | * Example: A compute() function, which results either in an Integer value (in the case of success) or 19 | * in an error message of type String (in the case of failure). By convention the success case is Right and the failure 20 | * is Left. 21 | * 22 | *

 23 |  * 
 24 |  * Either<String,Integer> value = compute().right().map(i -> i * 2).toEither();
 25 |  * 
 26 |  * 
27 | * 28 | * If the result of compute() is Right(1), the value is Right(2).
29 | * If the result of compute() is Left("error), the value is Left("error"). 30 | * 31 | * @param The type of the Left value of an Either. 32 | * @param The type of the Right value of an Either. 33 | * @author Daniel Dietrich 34 | * @since 1.0.0 35 | */ 36 | interface Either { 37 | 38 | /** 39 | * Returns a LeftProjection of this Either. 40 | * 41 | * @return a new LeftProjection of this 42 | */ 43 | fun left(): LeftProjection { 44 | return LeftProjection(this) 45 | } 46 | 47 | /** 48 | * Returns a RightProjection of this Either. 49 | * 50 | * @return a new RightProjection of this 51 | */ 52 | fun right(): RightProjection { 53 | return RightProjection(this) 54 | } 55 | 56 | /** 57 | * Returns whether this Either is a Left. 58 | * 59 | * @return true, if this is a Left, false otherwise 60 | */ 61 | fun isLeft(): Boolean 62 | 63 | /** 64 | * Returns the value of this. 65 | * 66 | * @return the value of this 67 | */ 68 | fun get(): Any? 69 | 70 | /** 71 | * Returns whether this Either is a Right. 72 | * 73 | * @return true, if this is a Right, false otherwise 74 | */ 75 | fun isRight(): Boolean 76 | 77 | /** 78 | * Maps either the left or the right side of this disjunction. 79 | * 80 | * @param leftMapper maps the left value if this is a Left 81 | * @param rightMapper maps the right value if this is a Right 82 | * @param The new left type of the resulting Either 83 | * @param The new right type of the resulting Either 84 | * @return A new either instance 85 | */ 86 | fun bimap(leftMapper: (L) -> X, rightMapper: (R) -> Y): Either 87 | 88 | /** 89 | * Converts a {@code Left} to a {@code Right} vice versa by wrapping the value in a new type. 90 | * 91 | * @return a new {@code Either} 92 | */ 93 | fun swap(): Either 94 | 95 | 96 | override fun equals(other: Any?): Boolean 97 | 98 | override fun hashCode(): Int 99 | 100 | override fun toString(): String 101 | 102 | /** 103 | * A left projection of an either. 104 | * 105 | * @param The type of the Left value of an Either. 106 | * @param The type of the Right value of an Either. 107 | * @since 1.0.0 108 | */ 109 | class LeftProjection constructor(val either: Either) : Value { 110 | 111 | override fun isEmpty(): Boolean { 112 | return either.isRight() 113 | } 114 | 115 | 116 | fun asLeft(): L { 117 | return (either as Left).get() 118 | } 119 | 120 | fun asRight(): R { 121 | return (either as Right).get() 122 | } 123 | 124 | /** 125 | * Gets the Left value or throws. 126 | * 127 | * @return the left value, if the underlying Either is a Left 128 | * @throws NoSuchElementException if the underlying either of this LeftProjection is a Right 129 | */ 130 | override fun get(): L { 131 | if (either.isLeft()) { 132 | return asLeft() 133 | } else { 134 | throw NoSuchElementException("Either.left().get() on Right") 135 | } 136 | } 137 | 138 | 139 | /** 140 | * Runs an action in the case this is a projection on a Right value. 141 | * 142 | * @param action an action which consumes a Right value 143 | */ 144 | fun orElseRun(action: (R) -> Unit) { 145 | Objects.requireNonNull(action, "action is null") 146 | if (either.isRight()) { 147 | action(asRight()) 148 | } 149 | } 150 | 151 | @Throws(Throwable::class) 152 | fun orElseThrow(exceptionFunction: (R) -> X): L { 153 | Objects.requireNonNull(exceptionFunction, "exceptionFunction is null") 154 | if (either.isLeft()) { 155 | return asLeft() 156 | } else { 157 | throw exceptionFunction(asRight()) 158 | } 159 | } 160 | 161 | /** 162 | * Returns the underlying either of this projection. 163 | * @return the underlying either 164 | */ 165 | fun toEither(): Either { 166 | return either 167 | } 168 | 169 | /** 170 | * Returns {@code Some} value of type L if this is a left projection of a Left value and the predicate 171 | * applies to the underlying value. 172 | * 173 | * @param predicate A predicate 174 | * @return A new Option 175 | */ 176 | override fun filter(predicate: (L) -> Boolean): Option { 177 | Objects.requireNonNull(predicate, "predicate is null") 178 | if (either.isLeft()) { 179 | val value = asLeft() 180 | return if (predicate(value)) Some(value) else None 181 | } else { 182 | return None 183 | } 184 | } 185 | 186 | /** 187 | * FlatMaps the left value if the projected Either is a Left. 188 | * 189 | * @param mapper A mapper which takes a left value and returns a java.lang.Iterable 190 | * @param The new type of a Left value 191 | * @return A new LeftProjection 192 | */ 193 | override fun flatMap(mapper: (L) -> Iterable): LeftProjection { 194 | if (either.isLeft()) { 195 | return Left(Value[mapper(asLeft())]).left() 196 | } else { 197 | @Suppress("CAST_NEVER_SUCCEEDS") 198 | return this as LeftProjection 199 | } 200 | } 201 | 202 | /** 203 | * Maps the left value if the projected Either is a Left. 204 | * 205 | * @param mapper A mapper which takes a left value and returns a value of type U 206 | * @param The new type of a Left value 207 | * @return A new LeftProjection 208 | */ 209 | override fun map(mapper: (L) -> U?): LeftProjection { 210 | if (either.isLeft()) { 211 | val result = mapper(asLeft()) 212 | if (result != null) { 213 | return Left(result).left() 214 | } else { 215 | @Suppress("CAST_NEVER_SUCCEEDS") 216 | return this as LeftProjection 217 | } 218 | } else { 219 | @Suppress("CAST_NEVER_SUCCEEDS") 220 | return this as LeftProjection 221 | } 222 | } 223 | 224 | /** 225 | * Applies the given action to the value if the projected either is a Left. Otherwise nothing happens. 226 | * 227 | * @param action An action which takes a left value 228 | * @return this LeftProjection 229 | */ 230 | override fun peek(action: (L) -> Unit): LeftProjection { 231 | if (either.isLeft()) { 232 | action(asLeft()) 233 | } 234 | return this 235 | } 236 | 237 | override fun iterator(): Iterator { 238 | if (either.isLeft()) { 239 | return iteratorOf(asLeft()) 240 | } else { 241 | return emptyIterator() 242 | } 243 | } 244 | 245 | override fun equals(other: Any?): Boolean { 246 | if (this === other) return true 247 | if (other?.javaClass != javaClass) return false 248 | 249 | other as LeftProjection<*, *> 250 | 251 | if (either != other.either) return false 252 | 253 | return true 254 | } 255 | 256 | override fun hashCode(): Int { 257 | return either.hashCode() 258 | } 259 | 260 | override fun toString(): String { 261 | return "LeftProjection($either)" 262 | } 263 | } 264 | 265 | class RightProjection constructor(val either: Either) : Value { 266 | 267 | override fun isEmpty(): Boolean { 268 | return either.isLeft() 269 | } 270 | 271 | 272 | fun asLeft(): L { 273 | return (either as Left).get() 274 | } 275 | 276 | fun asRight(): R { 277 | return (either as Right).get() 278 | } 279 | 280 | /** 281 | * Gets the Right value or throws. 282 | * 283 | * @return the left value, if the underlying Either is a Right 284 | * @throws NoSuchElementException if the underlying either of this RightProjection is a Left 285 | */ 286 | override fun get(): R { 287 | if (either.isRight()) { 288 | return asRight() 289 | } else { 290 | throw NoSuchElementException("Either.right().get() on Left") 291 | } 292 | } 293 | 294 | 295 | /** 296 | * Runs an action in the case this is a projection on a Left value. 297 | * 298 | * @param action an action which consumes a Left value 299 | */ 300 | fun orElseRun(action: (L) -> Unit) { 301 | if (either.isLeft()) { 302 | action(asLeft()) 303 | } 304 | } 305 | 306 | /** 307 | * Gets the Right value or throws, if the projected Either is a Left. 308 | * 309 | * @param a throwable type 310 | * @param exceptionFunction a function which creates an exception based on a Left value 311 | * @return the right value, if the underlying Either is a Right or else throws the exception provided by 312 | * {@code exceptionFunction} by applying the Left value. 313 | * @throws X if the projected Either is a Left 314 | */ 315 | @Throws(Throwable::class) 316 | fun orElseThrow(exceptionFunction: (L) -> X): R { 317 | Objects.requireNonNull(exceptionFunction, "exceptionFunction is null") 318 | if (either.isRight()) { 319 | return asRight() 320 | } else { 321 | throw exceptionFunction(asLeft()) 322 | } 323 | } 324 | 325 | /** 326 | * Returns the underlying either of this projection. 327 | * 328 | * @return the underlying either 329 | */ 330 | fun toEither(): Either { 331 | return either 332 | } 333 | 334 | /** 335 | * Returns {@code Some} value of type R if this is a right projection of a Right value and the predicate 336 | * applies to the underlying value. 337 | * 338 | * @param predicate A predicate 339 | * @return A new Option 340 | */ 341 | override fun filter(predicate: (R) -> Boolean): Option { 342 | if (either.isRight()) { 343 | val value = asRight() 344 | return if (predicate(value)) Some(value) else None 345 | } else { 346 | return None 347 | } 348 | } 349 | 350 | /** 351 | * FlatMaps the right value if the projected Either is a Right. 352 | * 353 | * @param mapper A mapper which takes a right value and returns a java.lang.Iterable 354 | * @param The new type of a Right value 355 | * @return A new RightProjection 356 | */ 357 | override fun flatMap(mapper: (R) -> Iterable): RightProjection { 358 | Objects.requireNonNull(mapper, "mapper is null") 359 | if (either.isRight()) { 360 | return Right(Value[mapper(asRight())]).right() 361 | } else { 362 | @Suppress("CAST_NEVER_SUCCEEDS") 363 | return this as RightProjection 364 | } 365 | } 366 | 367 | /** 368 | * Maps the right value if the projected Either is a Right. 369 | * 370 | * @param mapper A mapper which takes a right value and returns a value of type U 371 | * @param The new type of a Right value 372 | * @return A new RightProjection 373 | */ 374 | override fun map(mapper: (R) -> U?): RightProjection { 375 | if (either.isRight()) { 376 | val result = mapper(asRight()) 377 | if (result != null) { 378 | return Right(result).right() 379 | } else { 380 | @Suppress("CAST_NEVER_SUCCEEDS") 381 | return this as RightProjection 382 | } 383 | } else { 384 | @Suppress("CAST_NEVER_SUCCEEDS") 385 | return this as RightProjection 386 | } 387 | } 388 | 389 | /** 390 | * Applies the given action to the value if the projected either is a Right. Otherwise nothing happens. 391 | * 392 | * @param action An action which takes a right value 393 | * @return this {@code Either} instance 394 | */ 395 | override fun peek(action: (R) -> Unit): RightProjection { 396 | if (either.isRight()) { 397 | action(asRight()) 398 | } 399 | return this 400 | } 401 | 402 | override fun iterator(): Iterator { 403 | if (either.isRight()) { 404 | return iteratorOf(asRight()) 405 | } else { 406 | return emptyIterator() 407 | } 408 | } 409 | 410 | 411 | override fun toString(): String { 412 | return "RightProjection($either)" 413 | } 414 | 415 | override fun equals(other: Any?): Boolean { 416 | if (this === other) return true 417 | if (other?.javaClass != javaClass) return false 418 | 419 | other as RightProjection<*, *> 420 | 421 | if (either != other.either) return false 422 | 423 | return true 424 | } 425 | 426 | override fun hashCode(): Int { 427 | return either.hashCode() 428 | } 429 | } 430 | 431 | } 432 | 433 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/control/EitherExt.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | 4 | /** 5 | * [TODO: Documentation] 6 | * 7 | * @author Deny Prasetyo. 8 | */ 9 | 10 | 11 | /** 12 | * Gets the Left value or an alternate value, if the projected Either is a Right. 13 | * 14 | * @param other an alternative value 15 | * @return the left value, if the underlying Either is a Left or else {@code other} 16 | * @throws NoSuchElementException if the underlying either of this LeftProjection is a Right 17 | */ 18 | fun Either.LeftProjection.orElse(other: L): L { 19 | return if (either.isLeft()) asLeft() else other 20 | } 21 | 22 | /** 23 | * Gets the Left value or an alternate value, if the projected Either is a Right. 24 | * 25 | * @param other a function which converts a Right value to an alternative Left value 26 | * @return the left value, if the underlying Either is a Left or else the alternative Left value provided by 27 | * {@code other} by applying the Right value. 28 | */ 29 | fun Either.LeftProjection.orElseGet(other: (R) -> L): L { 30 | if (either.isLeft()) { 31 | return asLeft() 32 | } else { 33 | return other(asRight()) 34 | } 35 | } 36 | 37 | 38 | /** 39 | * Gets the Right value or an alternate value, if the projected Either is a Left. 40 | * 41 | * @param other an alternative value 42 | * @return the right value, if the underlying Either is a Right or else {@code other} 43 | * @throws NoSuchElementException if the underlying either of this RightProjection is a Left 44 | */ 45 | fun Either.RightProjection.orElse(other: R): R { 46 | return if (either.isRight()) asRight() else other 47 | } 48 | 49 | /** 50 | * Gets the Right value or an alternate value, if the projected Either is a Left. 51 | * 52 | * @param other a function which converts a Left value to an alternative Right value 53 | * @return the right value, if the underlying Either is a Right or else the alternative Right value provided by 54 | * {@code other} by applying the Left value. 55 | */ 56 | fun Either.RightProjection.orElseGet(other: (L) -> R): R { 57 | if (either.isRight()) { 58 | return asRight() 59 | } else { 60 | return other(asLeft()) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/control/Failure.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import java.io.Serializable 4 | 5 | 6 | /** 7 | * A failed Try. 8 | * 9 | * @param component type of this Failure 10 | * @property throwable, Throwable cause 11 | * @constructor Construct a Failure 12 | * @author Daniel Dietrich, Deny Prasetyo 13 | * @since 1.0.0 14 | */ 15 | 16 | final class Failure(private val throwable: Throwable) : Try, Serializable { 17 | 18 | override fun get(): T { 19 | throw throwable 20 | } 21 | 22 | override fun getCause(): Throwable { 23 | return throwable; 24 | } 25 | 26 | override fun isEmpty(): Boolean { 27 | return true 28 | } 29 | 30 | override fun isFailure(): Boolean { 31 | return true 32 | } 33 | 34 | override fun isSuccess(): Boolean { 35 | return false 36 | } 37 | 38 | override fun equals(other: Any?): Boolean { 39 | if (this === other) return true 40 | if (other != null && other is Failure<*> && throwable == other.throwable) return true 41 | return false 42 | } 43 | 44 | override fun hashCode(): Int { 45 | return throwable.hashCode() 46 | } 47 | 48 | override fun toString(): String { 49 | return "Failure($throwable)" 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/control/Initializer.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import java.util.NoSuchElementException 4 | import java.util.Optional 5 | 6 | /** 7 | * Create new Option from a Java Optional 8 | * 9 | * @param type of the value 10 | * @return {@code Some(optional.get())} if value is Java {@code Optional} is present, {@code None} otherwise 11 | */ 12 | fun Optional.toOption(): Option { 13 | return if (this.isPresent) optionOf(this.get()) else none() 14 | } 15 | 16 | /** 17 | * Creates a new {@code Option} from nullable Value. 18 | * 19 | * @param type of the nullable value 20 | * @return {@code Some(value)} if value is not {@code null}, {@code None} otherwise 21 | */ 22 | fun T?.toOption(): Option { 23 | return if (this != null) { 24 | Some(this) 25 | } else { 26 | None 27 | } 28 | } 29 | 30 | /** 31 | * Create new Try from a Java Optional 32 | * 33 | * @param type of the value 34 | * @return {@code Success(optional.get())} if value is Java {@code Optional} is present, {@code Failure(NoSuchElementException)} otherwise 35 | */ 36 | fun Optional.toTry(): Try { 37 | return if (this.isPresent) { 38 | this.get().toTry() 39 | } else { 40 | failure(NoSuchElementException("No value present")) 41 | } 42 | } 43 | 44 | /** 45 | * Creates a new {@code Try} from nullable Value. 46 | * 47 | * @param type of the nullable value 48 | * @return {@code Success(value)} if value is not {@code null}, {@code Failure(NoSuchElementException)} otherwise 49 | */ 50 | fun T?.toTry(): Try { 51 | return if (this != null) { 52 | success(this) 53 | } else { 54 | failure(NoSuchElementException("No value present")) 55 | } 56 | } 57 | 58 | /** 59 | * Creates a new {@code Option} of a given value. 60 | * 61 | * @param value A value, can be {@code null} 62 | * @param type of the value 63 | * @return {@code Some(value)} if value is not {@code null}, {@code None} otherwise 64 | */ 65 | fun optionOf(value: T?): Option { 66 | return if (value == null) None else Some(value) 67 | } 68 | 69 | /** 70 | * Creates a new {@code Some} of a given value. 71 | *

72 | * The only difference to {@link optionOf(Object)} is, cannot receive {@code null}. 73 | *

 74 |  * 
 75 |  * Option.of(null);   // = None
 76 |  * Option.some(null); // = Compile Error
 77 |  * 
 78 |  * 
79 | * 80 | * @param value A value 81 | * @param type of the value 82 | * @return {@code Some(value)} 83 | */ 84 | fun some(value: T): Option { 85 | return Some(value) 86 | } 87 | 88 | /** 89 | * Returns the single instance of {@code None} 90 | * 91 | * @param component type 92 | * @return the single instance of {@code None} 93 | */ 94 | fun none(): Option { 95 | return None 96 | } 97 | 98 | /** 99 | * Creates {@code Some} of suppliers value if condition is true, or {@code None} in other case 100 | * 101 | * @param type of the optional value 102 | * @param condition A boolean value 103 | * @param supplier An optional value supplier 104 | * @return return {@code Some} of supplier's value if condition is true, or {@code None} in other case 105 | */ 106 | fun optionWhen(condition: Boolean, supplier: () -> T): Option { 107 | return if (condition) optionOf(supplier()) else none() 108 | } 109 | 110 | /** 111 | * Creates a Try of a CheckedSupplier. 112 | * 113 | * @param supplier A checked supplier 114 | * @param Component type 115 | * @return {@code Success(supplier.get())} if no exception occurs, otherwise {@code Failure(throwable)} if an 116 | * exception occurs calling {@code supplier.get()}. 117 | */ 118 | fun tryOf(supplier: () -> T?): Try { 119 | return try { 120 | supplier().toTry() 121 | } catch (t: Throwable) { 122 | Failure(t) 123 | } 124 | } 125 | 126 | /** 127 | * Creates a Try of a Nullable value. 128 | * 129 | * @param value A nullable value. 130 | * @param Component type 131 | * @return {@code Success(value)} if value is not {@code null}, {@code Failure(NoSuchElementException)} otherwise 132 | * exception occurs calling {@code supplier.get()}. 133 | */ 134 | fun tryOf(value: T?): Try { 135 | return value.toTry() 136 | } 137 | 138 | /** 139 | * Creates a Try of a Runnable. 140 | * 141 | * @param runnable A checked runnable 142 | * @return {@code Success(Unit)} if no exception occurs, otherwise {@code Failure(throwable)} if an exception occurs 143 | * calling {@code runnable()}. 144 | */ 145 | 146 | fun tryRun(runnable: () -> Unit): Try { 147 | return try { 148 | runnable() 149 | Success(Unit) 150 | } catch (t: Throwable) { 151 | Failure(t) 152 | } 153 | } 154 | 155 | /** 156 | * Creates a {@link Success} that contains the given {@code value}. Shortcut for {@code new Success<>(value)}. 157 | * 158 | * @param value A value. 159 | * @param Type of the given {@code value}. 160 | * @return A new {@code Success}. 161 | */ 162 | fun success(value: T): Try { 163 | return Success(value) 164 | } 165 | 166 | /** 167 | * Creates a {@link Failure} that contains the given {@code exception}. Shortcut for {@code new Failure<>(exception)}. 168 | * 169 | * @param exception An exception. 170 | * @param Component type of the {@code Try}. 171 | * @return A new {@code Failure}. 172 | */ 173 | fun failure(exception: Throwable): Try { 174 | return Failure(exception) 175 | } 176 | 177 | /** 178 | * Constructs a {@link Right} 179 | * 180 | * @param right The value. 181 | * @param Type of left value. 182 | * @param Type of right value. 183 | * @return A new {@code Right} instance. 184 | */ 185 | fun right(right: R): Either { 186 | return Right(right) 187 | } 188 | 189 | /** 190 | * Constructs a {@link Left} 191 | * 192 | * @param left The value. 193 | * @param Type of left value. 194 | * @param Type of right value. 195 | * @return A new {@code Left} instance. 196 | */ 197 | fun left(left: L): Either { 198 | return Left(left) 199 | } 200 | 201 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/control/Left.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import java.io.Serializable 4 | 5 | 6 | /** 7 | * The {@code Left} version of an {@code Either}. 8 | * 9 | * @param left component type 10 | * @param right component type 11 | * @author Daniel Dietrich, Deny Prasetyo 12 | * @since 1.0.0 13 | */ 14 | 15 | final class Left(val value: L) : Either, Serializable { 16 | 17 | 18 | override fun isLeft(): Boolean { 19 | return true 20 | } 21 | 22 | override fun isRight(): Boolean { 23 | return false 24 | } 25 | 26 | /** 27 | * Returns the value of this {@code Left}. 28 | * 29 | * @return the value of this {@code Left} 30 | */ 31 | override fun get(): L { 32 | return value 33 | } 34 | 35 | /** 36 | * Wrap the value of this {@code Left} in a new {@code Right}. 37 | * 38 | * @return a new {@code Right} containing this value 39 | */ 40 | override fun swap(): Right { 41 | return Right(value) 42 | } 43 | 44 | override fun bimap(leftMapper: (L) -> X, rightMapper: (R) -> Y): Left { 45 | return Left(leftMapper(value)) 46 | } 47 | 48 | override fun equals(other: Any?): Boolean { 49 | if (this === other) return true 50 | if (other != null && other is Left<*, *> && value == other.value) return true 51 | return false 52 | } 53 | 54 | override fun hashCode(): Int { 55 | return value.hashCode() 56 | } 57 | 58 | override fun toString(): String { 59 | return "Left($value)" 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/control/None.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import java.io.Serializable 4 | import java.util.NoSuchElementException 5 | 6 | 7 | /** 8 | * None is a singleton representation of the {@Code Nothing} {@link kotlinslang.control.Option}. The instance is obtained by 9 | * calling {@link #instance()}. 10 | * 11 | * @param The type of the optional value. 12 | * @author Daniel Dietrich, Deny Prasetyo 13 | * @since 1.0.0 14 | */ 15 | 16 | object None : Option, Serializable { 17 | 18 | override fun get(): Nothing { 19 | throw NoSuchElementException("No value present") 20 | } 21 | 22 | override fun isEmpty(): Boolean { 23 | return true 24 | } 25 | 26 | override fun equals(other: Any?): Boolean { 27 | return other === this; 28 | } 29 | 30 | override fun hashCode(): Int { 31 | return 1 32 | } 33 | 34 | override fun toString(): String { 35 | return "None" 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/control/Option.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import kotlinslang.Value 4 | import kotlinslang.collection.emptyIterator 5 | import kotlinslang.collection.iteratorOf 6 | import kotlinslang.toOption 7 | 8 | 9 | /** 10 | * Replacement for {@link java.util.Optional}. 11 | *

16 | * Most of the API is taken from {@link java.util.Optional}. A similar type can be found in Haskell and Scala. 19 | * 20 | * @param The type of the optional value. 21 | * @author Daniel Dietrich, Deny Prasetyo 22 | * @since 1.0.0 23 | */ 24 | interface Option : Value { 25 | /** 26 | * Returns true, if this is {@code None}, otherwise false, if this is {@code Some}. 27 | * 28 | * @return true, if this {@code Option} is empty, false otherwise 29 | */ 30 | override fun isEmpty(): Boolean 31 | 32 | /** 33 | * Returns true, if this is {@code Some}, otherwise false, if this is {@code None}. 34 | *

35 | * Please note that it is possible to create {@code new Some(null)}, which is defined. 36 | * 37 | * @return true, if this {@code Option} has a defined value, false otherwise 38 | */ 39 | override fun isDefined(): Boolean { 40 | return !isEmpty() 41 | } 42 | 43 | 44 | override fun get(): T 45 | 46 | /** 47 | * Returns the value if this is a {@code Some}, otherwise throws an exception. 48 | * 49 | * @param supplier An exception supplier 50 | * @param A throwable 51 | * @return This value, if this Option is defined, otherwise throws X 52 | * @throws X a throwable 53 | */ 54 | @Throws(exceptionClasses = Throwable::class) 55 | override fun orElseThrow(supplier: () -> X): T { 56 | return super.orElseThrow(supplier) 57 | } 58 | 59 | /** 60 | * Returns {@code Some(value)} if this is a {@code Some} and the value satisfies the given predicate. 61 | * Otherwise {@code None} is returned. 62 | * 63 | * @param predicate A predicate which is used to test an optional value 64 | * @return {@code Some(value)} or {@code None} as specified 65 | */ 66 | override fun filter(predicate: (T) -> Boolean): Option { 67 | return if ((isEmpty() || predicate(get()))) this else None 68 | } 69 | 70 | /** 71 | * Maps the value to a new {@code Option} if this is a {@code Some}, otherwise returns {@code None}. 72 | * 73 | * @param mapper A mapper 74 | * @param Component type of the resulting Option 75 | * @return a new {@code Option} 76 | */ 77 | override fun flatMap(mapper: (T) -> Iterable): Option { 78 | if (isEmpty()) { 79 | return None 80 | } else { 81 | val iterable = mapper(get()) 82 | if (iterable is Value) { 83 | return iterable.toOption() 84 | } else { 85 | val iterator = iterable.iterator() 86 | if (iterator.hasNext()) { 87 | return Some(iterator.next()) 88 | } else { 89 | return None 90 | } 91 | } 92 | } 93 | } 94 | 95 | /** 96 | * Maps the value and wraps it in a new {@code Some} if this is a {@code Some}, returns {@code None}. 97 | * 98 | * @param mapper A value mapper 99 | * @param The new value type 100 | * @return a new {@code Some} containing the mapped value if this Option is defined, otherwise {@code None}, if this is empty. 101 | */ 102 | override fun map(mapper: (T) -> U?): Option { 103 | if (isEmpty()) { 104 | return None 105 | } else { 106 | val result = mapper(get()) 107 | if (result != null) { 108 | return Some(result) 109 | } else { 110 | return None 111 | } 112 | } 113 | } 114 | 115 | 116 | fun

map(p: Option

, f: (T, P) -> U): Option { 117 | return flatMap { t -> p.map { pp1 -> f(t, pp1) } } 118 | } 119 | 120 | /** 121 | * Applies an action to this value, if this option is defined, otherwise does nothing. 122 | * 123 | * @param action An action which can be applied to an optional value 124 | * @return this {@code Option} 125 | */ 126 | override fun peek(action: (T) -> Unit): Option { 127 | if (isDefined()) { 128 | action(get()) 129 | } 130 | return this 131 | } 132 | 133 | /** 134 | * Transforms this {@code Option}. 135 | * 136 | * @param function A transformation 137 | * @param Type of transformation result 138 | * @return An instance of type {@code U} 139 | */ 140 | fun transform(function: (Option) -> U): U { 141 | return function(this) 142 | } 143 | 144 | override fun iterator(): Iterator { 145 | return if (isEmpty()) emptyIterator() else iteratorOf(get()) 146 | } 147 | 148 | override fun equals(other: Any?): Boolean 149 | 150 | override fun hashCode(): Int 151 | 152 | override fun toString(): String 153 | 154 | } 155 | 156 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/control/Right.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import java.io.Serializable 4 | 5 | 6 | /** 7 | * The {@code Right} version of an {@code Either}. 8 | * 9 | * @param left component type 10 | * @param right component type 11 | * @author Daniel Dietrich, Deny Prasetyo 12 | * @since 1.0.0 13 | */ 14 | final class Right(val value: R) : Either, Serializable { 15 | 16 | override fun isLeft(): Boolean { 17 | return false 18 | } 19 | 20 | override fun isRight(): Boolean { 21 | return true 22 | } 23 | 24 | override fun bimap(leftMapper: (L) -> X, rightMapper: (R) -> Y): Right { 25 | return Right(rightMapper(value)) 26 | } 27 | 28 | /** 29 | * Returns the value of this {@code Right}. 30 | * 31 | * @return the value of this {@code Right} 32 | */ 33 | override fun get(): R { 34 | return value 35 | } 36 | 37 | /** 38 | * Wrap the value of this {@code Right} in a new {@code Left}. 39 | * 40 | * @return a new {@code Left} containing this value 41 | */ 42 | override fun swap(): Left { 43 | return Left(value) 44 | } 45 | 46 | override fun equals(other: Any?): Boolean { 47 | if (this === other) return true 48 | if (other != null && other is Right<*, *> && value == other.value) return true 49 | return false 50 | } 51 | 52 | override fun hashCode(): Int { 53 | return value.hashCode() 54 | } 55 | 56 | override fun toString(): String { 57 | return "Right($value)" 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/control/Some.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import java.io.Serializable 4 | 5 | 6 | /** 7 | * Some represents a defined {@link javaslang.control.Option}. It contains a value which may be null. However, to 8 | * create an Option containing null, {@code new Some(null)} has to be called. In all other cases 9 | * {@link Option#of(Object)} is sufficient. 10 | * 11 | * @param The type of the optional value. 12 | * @author Daniel Dietrich, Deny Prasetyo 13 | * @since 1.0.0 14 | */ 15 | final class Some(val value: T) : Option, Serializable { 16 | 17 | override fun get(): T { 18 | return value; 19 | } 20 | 21 | override fun isEmpty(): Boolean { 22 | return false 23 | } 24 | 25 | override fun toString(): String { 26 | return "Some($value)" 27 | } 28 | 29 | override fun equals(other: Any?): Boolean { 30 | if (this === other) return true 31 | if (other != null && other is Some<*> && value == other.value) return true 32 | return false 33 | } 34 | 35 | override fun hashCode(): Int { 36 | return value.hashCode() 37 | } 38 | 39 | 40 | } -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/control/Success.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import java.io.Serializable 4 | 5 | 6 | /** 7 | * A succeeded Try. 8 | * 9 | * @param component type of this Success 10 | * @property value The value of this Success 11 | * @constructor Constructs a Success 12 | * @author Daniel Dietrich, Deny Prasetyo 13 | * @since 1.0.0 14 | */ 15 | final class Success(val value: T) : Try, Serializable { 16 | 17 | override fun get(): T { 18 | return value 19 | } 20 | 21 | override fun getCause(): Throwable { 22 | throw UnsupportedOperationException("getCause on Success") 23 | } 24 | 25 | override fun isEmpty(): Boolean { 26 | return false 27 | } 28 | 29 | override fun isFailure(): Boolean { 30 | return false 31 | } 32 | 33 | override fun isSuccess(): Boolean { 34 | return true 35 | } 36 | 37 | override fun equals(other: Any?): Boolean { 38 | if (this === other) return true 39 | if (other != null && other is Success<*> && value == other.value) return true 40 | return false 41 | } 42 | 43 | 44 | override fun hashCode(): Int { 45 | return value.hashCode() 46 | } 47 | 48 | override fun toString(): String { 49 | return "Success($value)" 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/control/Try.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import kotlinslang.Value 4 | import kotlinslang.collection.emptyIterator 5 | import kotlinslang.collection.iteratorOf 6 | import kotlinslang.toTry 7 | import java.util.NoSuchElementException 8 | 9 | 10 | /** 11 | * An implementation similar to Scala's Try control. 12 | * 13 | * @param Value type in the case of success. 14 | * @author Daniel Dietrich, Deny Prasetyo 15 | * @since 1.0.0 16 | */ 17 | interface Try : Value { 18 | 19 | /** 20 | * Gets the result of this Try if this is a Success or throws if this is a Failure. 21 | * 22 | * @return The result of this Try. 23 | * @throws Throwable if this is a Failure 24 | */ 25 | override fun get(): T 26 | 27 | /** 28 | * Gets the cause if this is a Failure or throws if this is a Success. 29 | * 30 | * @return The cause if this is a Failure 31 | * @throws UnsupportedOperationException if this is a Success 32 | */ 33 | fun getCause(): Throwable 34 | 35 | /** 36 | * Checks whether this Try has no result, i.e. is a Failure. 37 | * 38 | * @return true if this is a Failure, returns false if this is a Success. 39 | */ 40 | override fun isEmpty(): Boolean 41 | 42 | /** 43 | * Checks if this is a Failure. 44 | * 45 | * @return true, if this is a Failure, otherwise false, if this is a Success 46 | */ 47 | fun isFailure(): Boolean 48 | 49 | /** 50 | * Checks if this is a Success. 51 | * 52 | * @return true, if this is a Success, otherwise false, if this is a Failure 53 | */ 54 | fun isSuccess(): Boolean 55 | 56 | 57 | /** 58 | * Runs the given checked consumer if this is a {@code Success}, 59 | * passing the result of the current expression to it. 60 | * If this expression is a {@code Failure} then it'll return a new 61 | * {@code Failure} of type T with the original exception. 62 | * 63 | * The main use case is chaining checked functions using method references: 64 | * 65 | *

 66 |      * 
 67 |      * Try.of(() -> 100)
 68 |      *    .andThen(i -> System.out.println(i));
 69 |      *
 70 |      * 
 71 |      * 
72 | * 73 | * @param consumer A checked consumer taking a single argument. 74 | * @return a new {@code Try} 75 | */ 76 | 77 | fun andThen(consumer: (T) -> Unit): Try { 78 | if (isFailure()) { 79 | return this 80 | } else { 81 | return tryRun { consumer(get()) }.flatMap { ignored -> this } 82 | } 83 | } 84 | 85 | /** 86 | * Runs the given runnable if this is a {@code Success}, otherwise returns this {@code Failure}. 87 | * Shorthand for {@code flatMap(ignored -> Try.run(runnable))}. 88 | * The main use case is chaining runnables using method references: 89 | * 90 | *
 91 |      * 
 92 |      * Try.run(A::methodRef).andThen(B::methodRef).andThen(C::methodRef);
 93 |      * 
 94 |      * 
95 | * 96 | * Please note that these lines are semantically the same: 97 | * 98 | *
 99 |      * 
100 |      * Try.run(() -> { doStuff(); })
101 |      *    .andThen(() -> { doMoreStuff(); })
102 |      *    .andThen(() -> { doEvenMoreStuff(); });
103 |      *
104 |      * Try.run(() -> {
105 |      *     doStuff();
106 |      *     doMoreStuff();
107 |      *     doEvenMoreStuff();
108 |      * });
109 |      * 
110 |      * 
111 | * 112 | * @param runnable A checked runnable 113 | * @return a new {@code Try} 114 | */ 115 | fun andThen(runnable: () -> Unit): Try { 116 | return flatMap { ignored -> tryRun(runnable) } 117 | } 118 | 119 | /** 120 | * Returns {@code Success(throwable)} if this is a {@code Failure(throwable)}, otherwise 121 | * a {@code Failure(new NoSuchElementException("Success.failed()"))} if this is a Success. 122 | * 123 | * @return a new Try 124 | */ 125 | fun failed(): Try { 126 | if (isFailure()) { 127 | return Success(getCause()) 128 | } else { 129 | return Failure(NoSuchElementException("Success.failed()")) 130 | } 131 | } 132 | 133 | /** 134 | * Returns {@code this} if this is a Failure or this is a Success and the value satisfies the predicate. 135 | *

136 | * Returns a new Failure, if this is a Success and the value does not satisfy the Predicate or an exception 137 | * occurs testing the predicate. 138 | * 139 | * @param predicate A predicate 140 | * @return a new Try 141 | */ 142 | override fun filter(predicate: (T) -> Boolean): Try { 143 | if (isFailure()) { 144 | return this 145 | } else { 146 | try { 147 | if (predicate(get())) { 148 | return this 149 | } else { 150 | return Failure(NoSuchElementException("Predicate does not hold for " + get())) 151 | } 152 | } catch (t: Throwable) { 153 | return Failure(t) 154 | } 155 | 156 | } 157 | } 158 | 159 | /** 160 | * FlatMaps the value of a Success or returns a Failure. 161 | * 162 | * @param mapper A mapper 163 | * @param The new component type 164 | * @return a new Try 165 | */ 166 | override fun flatMap(mapper: (T) -> Iterable): Try { 167 | if (isFailure()) { 168 | @Suppress("UNCHECKED_CAST") 169 | return this as Failure 170 | } else { 171 | try { 172 | val iterable = mapper(get()) 173 | if (iterable is Value) { 174 | return iterable.toTry() 175 | } else { 176 | return tryOf { Value[iterable] } 177 | } 178 | } catch (t: Throwable) { 179 | return Failure(t) 180 | } 181 | 182 | } 183 | } 184 | 185 | /** 186 | * Runs the given checked function if this is a {@code Success}, 187 | * passing the result of the current expression to it. 188 | * If this expression is a {@code Failure} then it'll return a new 189 | * {@code Failure} of type R with the original exception. 190 | * 191 | * The main use case is chaining checked functions using method references: 192 | * 193 | *

194 |      * 
195 |      * Try.of(() -> 0)
196 |      *    .mapTry(x -> 1 / x); // division by zero
197 |      * 
198 |      * 
199 | * 200 | * @param The new component type 201 | * @param mapper A checked function 202 | * @return a new {@code Try} 203 | */ 204 | override fun map(mapper: (T) -> U?): Try { 205 | if (isFailure()) { 206 | @Suppress("UNCHECKED_CAST") 207 | return this as Failure 208 | } else { 209 | return tryOf { mapper(get()) } 210 | } 211 | } 212 | 213 | override fun iterator(): Iterator { 214 | return if (isSuccess()) iteratorOf(get()) else emptyIterator() 215 | } 216 | 217 | /** 218 | * Consumes the throwable if this is a Failure. 219 | * 220 | * @param action An exception consumer 221 | * @return a new Failure, if this is a Failure and the consumer throws, otherwise this, which may be a Success or 222 | * a Failure. 223 | */ 224 | fun onFailure(action: (Throwable) -> Unit): Try { 225 | if (isFailure()) { 226 | try { 227 | action(getCause()) 228 | return this 229 | } catch (t: Throwable) { 230 | return Failure(t) 231 | } 232 | 233 | } else { 234 | return this 235 | } 236 | } 237 | 238 | /** 239 | * Consumes the value if this is a Success. 240 | * 241 | * @param action A value consumer 242 | * @return a new Failure, if this is a Success and the consumer throws, otherwise this, which may be a Success or 243 | * a Failure. 244 | */ 245 | fun onSuccess(action: (T) -> Unit): Try { 246 | if (isSuccess()) { 247 | try { 248 | action(get()) 249 | return this 250 | } catch (t: Throwable) { 251 | return Failure(t) 252 | } 253 | 254 | } else { 255 | return this 256 | } 257 | } 258 | 259 | fun orElseRun(action: (Throwable) -> Unit) { 260 | if (isFailure()) { 261 | action(getCause()) 262 | } 263 | } 264 | 265 | @Throws(Throwable::class) 266 | fun orElseThrow(exceptionProvider: (Throwable) -> X): T { 267 | if (isFailure()) { 268 | throw exceptionProvider(getCause()) 269 | } else { 270 | return get() 271 | } 272 | } 273 | 274 | /** 275 | * Applies the action to the value of a Success or does nothing in the case of a Failure. 276 | * 277 | * @param action A Consumer 278 | * @return this Try 279 | */ 280 | override fun peek(action: (T) -> Unit): Try { 281 | return onSuccess(action) 282 | } 283 | 284 | 285 | override fun equals(other: Any?): Boolean 286 | 287 | override fun hashCode(): Int 288 | 289 | override fun toString(): String 290 | 291 | } 292 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/control/TryExt.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | 4 | /** 5 | * [TODO: Documentation] 6 | * 7 | * @author Deny Prasetyo. 8 | */ 9 | 10 | 11 | fun Try.orElseGet(other: (Throwable) -> T): T { 12 | if (isFailure()) { 13 | return other(getCause()) 14 | } else { 15 | return get() 16 | } 17 | } 18 | 19 | /** 20 | * Returns {@code this}, if this is a {@code Success}, otherwise tries to recover the exception of the failure with {@code f}, 21 | * i.e. calling {@code Try.of(() -> f.apply(throwable))}. 22 | * 23 | * @param f A recovery function taking a Throwable 24 | * @return a new Try 25 | */ 26 | fun Try.recover(f: (Throwable) -> T): Try { 27 | if (isFailure()) { 28 | return tryOf { f(getCause()) } 29 | } else { 30 | return this 31 | } 32 | } 33 | 34 | /** 35 | * Returns {@code this}, if this is a Success, otherwise tries to recover the exception of the failure with {@code f}, 36 | * i.e. calling {@code f.apply(cause.getCause())}. If an error occurs recovering a Failure, then the new Failure is 37 | * returned. 38 | * 39 | * @param f A recovery function taking a Throwable 40 | * @return a new Try 41 | */ 42 | fun Try.recoverWith(f: (Throwable) -> Try): Try { 43 | if (isFailure()) { 44 | return try { 45 | f(getCause()) 46 | } catch (t: Throwable) { 47 | Failure(t) 48 | } 49 | 50 | } else { 51 | return this 52 | } 53 | } 54 | 55 | fun Try.toEither(): Either { 56 | if (isFailure()) { 57 | return Left(getCause()) 58 | } else { 59 | return Right(get()) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/function/Currying.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.function 2 | 3 | 4 | /** 5 | * Extension for Convert {@code Function[2 - 7]} to Curried Function and Vice-versa. 6 | * 7 | * @author Mario Arias, Deny Prasetyo. 8 | * @since 1.0.0 9 | */ 10 | 11 | 12 | fun Function2.curried(): (T1) -> (T2) -> R { 13 | return { t1: T1 -> { t2: T2 -> this(t1, t2) } } 14 | } 15 | 16 | 17 | fun Function3.curried(): (T1) -> (T2) -> (T3) -> R { 18 | return { t1: T1 -> { t2: T2 -> { t3: T3 -> this(t1, t2, t3) } } } 19 | } 20 | 21 | 22 | fun Function4.curried(): (T1) -> (T2) -> (T3) -> (T4) -> R { 23 | return { t1: T1 -> { t2: T2 -> { t3: T3 -> { t4: T4 -> this(t1, t2, t3, t4) } } } } 24 | } 25 | 26 | 27 | fun Function5.curried(): (T1) -> (T2) -> (T3) -> (T4) -> (T5) -> R { 28 | return { t1: T1 -> { t2: T2 -> { t3: T3 -> { t4: T4 -> { t5: T5 -> this(t1, t2, t3, t4, t5) } } } } } 29 | } 30 | 31 | 32 | fun Function6.curried(): (T1) -> (T2) -> (T3) -> (T4) -> (T5) -> (T6) -> R { 33 | return { t1: T1 -> { t2: T2 -> { t3: T3 -> { t4: T4 -> { t5: T5 -> { t6: T6 -> this(t1, t2, t3, t4, t5, t6) } } } } } } 34 | } 35 | 36 | 37 | fun Function7.curried(): (T1) -> (T2) -> (T3) -> (T4) -> (T5) -> (T6) -> (T7) -> R { 38 | return { t1: T1 -> { t2: T2 -> { t3: T3 -> { t4: T4 -> { t5: T5 -> { t6: T6 -> { t7: T7 -> this(t1, t2, t3, t4, t5, t6, t7) } } } } } } } 39 | } 40 | 41 | 42 | fun ((T1) -> (T2) -> R).uncurried(): (T1, T2) -> R { 43 | return { t1: T1, t2: T2 -> this(t1)(t2) } 44 | } 45 | 46 | 47 | fun ((T1) -> (T2) -> (T3) -> R).uncurried(): (T1, T2, T3) -> R { 48 | return { t1: T1, t2: T2, t3: T3 -> this(t1)(t2)(t3) } 49 | } 50 | 51 | 52 | fun ((T1) -> (T2) -> (T3) -> (T4) -> R).uncurried(): (T1, T2, T3, T4) -> R { 53 | return { t1: T1, t2: T2, t3: T3, t4: T4 -> this(t1)(t2)(t3)(t4) } 54 | } 55 | 56 | 57 | fun ((T1) -> (T2) -> (T3) -> (T4) -> (T5) -> R).uncurried(): (T1, T2, T3, T4, T5) -> R { 58 | return { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5 -> this(t1)(t2)(t3)(t4)(t5) } 59 | } 60 | 61 | 62 | fun ((T1) -> (T2) -> (T3) -> (T4) -> (T5) -> (T6) -> R).uncurried(): (T1, T2, T3, T4, T5, T6) -> R { 63 | return { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6 -> this(t1)(t2)(t3)(t4)(t5)(t6) } 64 | } 65 | 66 | 67 | fun ((T1) -> (T2) -> (T3) -> (T4) -> (T5) -> (T6) -> (T7) -> R).uncurried(): (T1, T2, T3, T4, T5, T6, T7) -> R { 68 | return { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7 -> this(t1)(t2)(t3)(t4)(t5)(t6)(t7) } 69 | } -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/function/FunctionExt.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.function 2 | 3 | 4 | /** 5 | * Helpers and Extension Functions that applied to Function1 class. 6 | * 7 | * @author Daniel Dietrich, Deny Prasetyo. 8 | * @since 1.0.0 9 | */ 10 | 11 | 12 | /** 13 | * Returns a composed function that first applies the {@code before} 14 | * function to its input, and then applies this function to the result. 15 | * If evaluation of either function throws an exception, it is relayed to 16 | * the caller of the composed function. 17 | * 18 | * @param the type of {@code before} function 19 | * @param the type of this {@code Function1} 20 | * @param before the function to apply before this function is applied 21 | * @return a composed function with type that first applies the {@code before} 22 | * function and then applies this function 23 | * 24 | * @see #andThen(Function1) 25 | */ 26 | infix fun Function1.compose(before: (V) -> T): (V) -> R { 27 | return { v: V -> this(before(v)) } 28 | } 29 | 30 | /** 31 | * Returns a composed function that first applies this function to 32 | * its input, and then applies the {@code after} function to the result. 33 | * If evaluation of either function throws an exception, it is relayed to 34 | * the caller of the composed function. 35 | * 36 | * @param the type of {@code after} function 37 | * @param the type of this {@code Function1} 38 | * @param after the function to apply after this function is applied 39 | * @return a composed function with type that first applies this function and then 40 | * applies the {@code after} function 41 | * 42 | * @see #compose(Function1) 43 | */ 44 | infix fun Function1.forwardCompose(after: (R) -> V): (T) -> V = andThen(after) 45 | 46 | /** 47 | * Returns a composed function that first applies this function to 48 | * its input, and then applies the {@code after} function to the result. 49 | * If evaluation of either function throws an exception, it is relayed to 50 | * the caller of the composed function. 51 | * 52 | * @param the type of {@code after} function 53 | * @param the type of this {@code Function1} 54 | * @param after the function to apply after this function is applied 55 | * @return a composed function with type that first applies this function and then 56 | * applies the {@code after} function 57 | * 58 | * @see #compose(Function1) 59 | * @see #forwardCompose(Function1) 60 | */ 61 | infix fun Function1.andThen(after: (R) -> V): (T) -> V { 62 | return { t: T -> after(this(t)) } 63 | } 64 | 65 | /** 66 | * Returns a function that always returns its input argument. 67 | * 68 | * @param the type of the input and output objects to the function 69 | * @return a function that always returns its input argument 70 | */ 71 | fun identity(): (T) -> T { 72 | return { t: T -> t } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/kotlin/kotlinslang/function/Reverse.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.function 2 | 3 | 4 | /** 5 | * Extensions for {@code Function[2-7]} to reverse Function. The order of parameter will be reversed. 6 | *

7 | * {@code (Int,Double) -> String} will be converted to {@code (Double,Int) -> String} 8 | * 9 | * @author Mario Arias, Deny Prasetyo. 10 | * @since 1.0.0 11 | */ 12 | 13 | 14 | fun Function2.reverse(): (T2, T1) -> R { 15 | return { t2: T2, t1: T1 -> this(t1, t2) } 16 | } 17 | 18 | 19 | fun Function3.reverse(): (T3, T2, T1) -> R { 20 | return { t3: T3, t2: T2, t1: T1 -> this(t1, t2, t3) } 21 | } 22 | 23 | 24 | fun Function4.reverse(): (T4, T3, T2, T1) -> R { 25 | return { t4: T4, t3: T3, t2: T2, t1: T1 -> this(t1, t2, t3, t4) } 26 | } 27 | 28 | 29 | fun Function5.reverse(): (T5, T4, T3, T2, T1) -> R { 30 | return { t5: T5, t4: T4, t3: T3, t2: T2, t1: T1 -> this(t1, t2, t3, t4, t5) } 31 | } 32 | 33 | 34 | fun Function6.reverse(): (T6, T5, T4, T3, T2, T1) -> R { 35 | return { t6: T6, t5: T5, t4: T4, t3: T3, t2: T2, t1: T1 -> this(t1, t2, t3, t4, t5, t6) } 36 | } 37 | 38 | 39 | fun Function7.reverse(): (T7, T6, T5, T4, T3, T2, T1) -> R { 40 | return { t7: T7, t6: T6, t5: T5, t4: T4, t3: T3, t2: T2, t1: T1 -> this(t1, t2, t3, t4, t5, t6, t7) } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/test/kotlin/kotlinslang/UtilTest.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang 2 | 3 | import kotlinslang.collection.emptyIterator 4 | import kotlinslang.collection.iteratorOf 5 | import kotlinslang.function.andThen 6 | import kotlinslang.function.compose 7 | import kotlinslang.function.forwardCompose 8 | import kotlinslang.function.identity 9 | import org.assertj.core.api.Assertions.assertThat 10 | import org.junit.Test 11 | import java.util.NoSuchElementException 12 | import kotlin.test.assertFailsWith 13 | 14 | 15 | /** 16 | * [TODO: Documentation] 17 | * 18 | * @author Deny Prasetyo. 19 | */ 20 | 21 | class UtilTest { 22 | 23 | @Test 24 | fun emptyIteratorThrowsException() { 25 | val emptyIter = emptyIterator() 26 | assertThat(emptyIter.hasNext()).isFalse() 27 | assertFailsWith( 28 | exceptionClass = NoSuchElementException::class, 29 | block = { emptyIter.next() }, 30 | message = "Throws Exception when call next() on EmptyIterator" 31 | ) 32 | } 33 | 34 | @Test 35 | fun iteratorOfProduceIterator() { 36 | val element = 10 37 | val nonEmptyIter = iteratorOf(element) 38 | assertThat(nonEmptyIter.hasNext()).isTrue() 39 | assertThat(nonEmptyIter.next()).isEqualTo(element) 40 | assertThat(nonEmptyIter.hasNext()).isFalse() 41 | 42 | val pair: Pair = Pair(10, 42) 43 | val nonEmptyPairIter = iteratorOf(pair.first, pair.second) 44 | assertThat(nonEmptyPairIter.hasNext()).isTrue() 45 | assertThat(nonEmptyPairIter.next()).isEqualTo(pair.first) 46 | 47 | assertThat(nonEmptyPairIter.hasNext()).isTrue() 48 | assertThat(nonEmptyPairIter.next()).isEqualTo(pair.second) 49 | assertThat(nonEmptyPairIter.hasNext()).isFalse() 50 | 51 | } 52 | 53 | 54 | @Test 55 | fun identityFunctionCorrectResult() { 56 | val initial = 10 57 | val identityFunction = identity() 58 | assertThat(identityFunction(initial)).isEqualTo(initial) 59 | } 60 | 61 | @Test 62 | fun listIsIterable() { 63 | val list = listOf(5, 6, 3, 3, 4, 2) 64 | val check = when (list) { 65 | is Iterable<*> -> true 66 | else -> false 67 | } 68 | 69 | assertThat(check).isTrue() 70 | } 71 | 72 | @Test 73 | fun forwardComposeAndThenBehaveCorrectly() { 74 | val functionOne = { i: Int -> i * 1.0 } 75 | val functionTwo = { d: Double -> d.toString() } 76 | 77 | val initial = 10 78 | val expected = "10.0" 79 | val forwardComposed = functionOne forwardCompose functionTwo 80 | 81 | assertThat(forwardComposed(initial)).isEqualTo(expected) 82 | 83 | val andThenFunction = functionOne andThen functionTwo 84 | assertThat(andThenFunction(initial)).isEqualTo(expected) 85 | } 86 | 87 | @Test 88 | fun composeBehaveCorrectly() { 89 | val functionOne = { i: Int -> i * 1.0 } 90 | val beforeFunction = { s: String -> s.toInt() } 91 | 92 | val initial = "42" 93 | val expected = 42.0 94 | val composed = functionOne compose beforeFunction 95 | 96 | assertThat(composed(initial)).isEqualTo(expected) 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /src/test/kotlin/kotlinslang/ValueTest.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang 2 | 3 | import kotlinslang.control.failure 4 | import kotlinslang.control.optionOf 5 | import kotlinslang.control.orElseGet 6 | import kotlinslang.control.some 7 | import kotlinslang.control.success 8 | import org.assertj.core.api.Assertions.assertThat 9 | import org.junit.Test 10 | import java.util.NoSuchElementException 11 | import kotlin.test.assertFailsWith 12 | 13 | 14 | /** 15 | * [TODO: Documentation] 16 | * 17 | * @author Deny Prasetyo. 18 | */ 19 | 20 | class ValueTest { 21 | 22 | @Test 23 | fun staticGetBehaveCorrectly() { 24 | val value = 42 25 | val option = optionOf(value) 26 | assertThat(Value[option]).isEqualTo(value) 27 | 28 | val list = listOf(value) 29 | assertThat(Value[list]).isEqualTo(value) 30 | } 31 | 32 | @Test 33 | fun getOptionBehaveCorrectly() { 34 | val element = 42 35 | val some = optionOf(element) 36 | assertThat(some.toOption()).isEqualTo(some) 37 | 38 | val success = success(element) 39 | assertThat(success.toOption()).isEqualTo(some) 40 | 41 | val none = optionOf(null) 42 | assertThat(none.toOption()).isEqualTo(none) 43 | 44 | val failure = failure(NoSuchElementException("No Element")) 45 | assertThat(failure.toOption()).isEqualTo(none) 46 | } 47 | 48 | @Test 49 | fun toTryBehaveCorrectly() { 50 | val element = 42 51 | val option = optionOf(element) 52 | val expected = success(element) 53 | 54 | val tryFromOption = option.toTry() 55 | assertThat(tryFromOption).isEqualTo(expected) 56 | assertThat(tryFromOption.get()).isEqualTo(element) 57 | 58 | assertThat(expected.toTry()).isEqualTo(expected) 59 | 60 | val none = optionOf(null) 61 | 62 | val tryFromNone = none.toTry() 63 | assertThat(tryFromNone.getCause()).isExactlyInstanceOf(NoSuchElementException::class.java) 64 | assertThat(tryFromNone.toTry()).isEqualTo(tryFromNone) 65 | assertFailsWith( 66 | exceptionClass = NoSuchElementException::class, 67 | block = { tryFromNone.get() } 68 | ) 69 | } 70 | 71 | @Test 72 | fun orElseFromEmptyBehaveCorrectly() { 73 | val elseValue = 42 74 | val none = optionOf(null) 75 | val failure = failure(ArithmeticException("Arithmetic Exception")) 76 | 77 | assertThat(none.orElse(elseValue)).isEqualTo(elseValue) 78 | assertThat(failure.orElse(elseValue)).isEqualTo(elseValue) 79 | 80 | assertThat(none.orElseGet({ elseValue })).isEqualTo(elseValue) 81 | assertThat(failure.orElseGet({ Unit -> elseValue })).isEqualTo(elseValue) 82 | assertThat(failure.orElseGet({ t: Throwable -> elseValue })).isEqualTo(elseValue) 83 | 84 | assertFailsWith( 85 | exceptionClass = NullPointerException::class, 86 | block = { none.orElseThrow({ NullPointerException("NPE") }) } 87 | ) 88 | 89 | assertFailsWith( 90 | exceptionClass = NullPointerException::class, 91 | block = { failure.orElseThrow({ t -> NullPointerException("NPE") }) } 92 | ) 93 | 94 | } 95 | 96 | @Test 97 | fun orElseFromNonEmptyBehaveCorrectly() { 98 | val element = 42 99 | val some = optionOf(element) 100 | val success = success(element) 101 | 102 | assertThat(some.orElse(element)).isEqualTo(element) 103 | assertThat(success.orElse(element)).isEqualTo(element) 104 | 105 | assertThat(some.orElseGet({ element })).isEqualTo(element) 106 | assertThat(success.orElseGet({ Unit -> element })).isEqualTo(element) 107 | assertThat(success.orElseGet({ t: Throwable -> element })).isEqualTo(element) 108 | 109 | assertThat(some.orElseThrow { NullPointerException("NPE") }).isEqualTo(element) 110 | assertThat(success.orElseThrow({ t -> NullPointerException("NPE") })).isEqualTo(element) 111 | 112 | } 113 | 114 | 115 | @Test 116 | fun ifDefineIfEmptyFromEmptyBehaveCorrectly() { 117 | 118 | val falseValue = 24 119 | val trueValue = 42 120 | val none = optionOf(null) 121 | val failure = failure(ArithmeticException("Arithmetic Exception")) 122 | 123 | assertThat(none.ifDefined(trueValue, falseValue)).isEqualTo(falseValue) 124 | assertThat(none.ifDefined({ trueValue }, { falseValue })).isEqualTo(falseValue) 125 | assertThat(none.ifDefined(trueValue, falseValue)).isNotEqualTo(trueValue) 126 | assertThat(none.ifDefined({ trueValue }, { falseValue })).isNotEqualTo(trueValue) 127 | 128 | assertThat(failure.ifDefined(trueValue, falseValue)).isEqualTo(falseValue) 129 | assertThat(failure.ifDefined({ trueValue }, { falseValue })).isEqualTo(falseValue) 130 | assertThat(failure.ifDefined(trueValue, falseValue)).isNotEqualTo(trueValue) 131 | assertThat(failure.ifDefined({ trueValue }, { falseValue })).isNotEqualTo(trueValue) 132 | 133 | assertThat(none.ifEmpty(trueValue, falseValue)).isEqualTo(trueValue) 134 | assertThat(none.ifEmpty({ trueValue }, { falseValue })).isEqualTo(trueValue) 135 | assertThat(none.ifEmpty(trueValue, falseValue)).isNotEqualTo(falseValue) 136 | assertThat(none.ifEmpty({ trueValue }, { falseValue })).isNotEqualTo(falseValue) 137 | 138 | assertThat(failure.ifEmpty(trueValue, falseValue)).isEqualTo(trueValue) 139 | assertThat(failure.ifEmpty({ trueValue }, { falseValue })).isEqualTo(trueValue) 140 | assertThat(failure.ifEmpty(trueValue, falseValue)).isNotEqualTo(falseValue) 141 | assertThat(failure.ifEmpty({ trueValue }, { falseValue })).isNotEqualTo(falseValue) 142 | } 143 | 144 | @Test 145 | fun ifDefineIfEmptyFromNonEmptyBehaveCorrectly() { 146 | 147 | val element = 142 148 | val falseValue = 24 149 | val trueValue = 42 150 | val some = optionOf(element) 151 | val success = success(element) 152 | 153 | assertThat(some.ifDefined(trueValue, falseValue)).isEqualTo(trueValue) 154 | assertThat(some.ifDefined({ trueValue }, { falseValue })).isEqualTo(trueValue) 155 | assertThat(some.ifDefined(trueValue, falseValue)).isNotEqualTo(falseValue) 156 | assertThat(some.ifDefined({ trueValue }, { falseValue })).isNotEqualTo(falseValue) 157 | 158 | assertThat(success.ifDefined(trueValue, falseValue)).isEqualTo(trueValue) 159 | assertThat(success.ifDefined({ trueValue }, { falseValue })).isEqualTo(trueValue) 160 | assertThat(success.ifDefined(trueValue, falseValue)).isNotEqualTo(falseValue) 161 | assertThat(success.ifDefined({ trueValue }, { falseValue })).isNotEqualTo(falseValue) 162 | 163 | assertThat(some.ifEmpty(trueValue, falseValue)).isEqualTo(falseValue) 164 | assertThat(some.ifEmpty({ trueValue }, { falseValue })).isEqualTo(falseValue) 165 | assertThat(some.ifEmpty(trueValue, falseValue)).isNotEqualTo(trueValue) 166 | assertThat(some.ifEmpty({ trueValue }, { falseValue })).isNotEqualTo(trueValue) 167 | 168 | assertThat(success.ifEmpty(trueValue, falseValue)).isEqualTo(falseValue) 169 | assertThat(success.ifEmpty({ trueValue }, { falseValue })).isEqualTo(falseValue) 170 | assertThat(success.ifEmpty(trueValue, falseValue)).isNotEqualTo(trueValue) 171 | assertThat(success.ifEmpty({ trueValue }, { falseValue })).isNotEqualTo(trueValue) 172 | } 173 | 174 | @Test 175 | fun existAndForAllFromEmptyBehaveCorrectly() { 176 | val none = optionOf(null) 177 | val nil = emptyList() 178 | val failure = failure(ArithmeticException("Arithmetic Exception")) 179 | 180 | assertThat(none.any({ t -> true })).isEqualTo(nil.any { t -> true }) 181 | assertThat(none.all({ t -> true })).isEqualTo(nil.all { t -> true }) 182 | 183 | assertThat(failure.any({ t -> true })).isEqualTo(nil.any { t -> true }) 184 | assertThat(failure.all({ t -> true })).isEqualTo(nil.all { t -> true }) 185 | } 186 | 187 | @Test 188 | fun existAndForAllFromNonEmptyBehaveCorrectly() { 189 | val element = 42 190 | val list = listOf(element) 191 | val some = some(element) 192 | val success = success(element) 193 | 194 | assertThat(some.any({ t -> false })).isEqualTo(list.any { t -> false }) 195 | assertThat(some.all({ t -> true })).isEqualTo(list.all { t -> true }) 196 | 197 | assertThat(success.any({ t -> true })).isEqualTo(list.any { t -> true }) 198 | assertThat(success.all({ t -> false })).isEqualTo(list.all { t -> false }) 199 | } 200 | } -------------------------------------------------------------------------------- /src/test/kotlin/kotlinslang/algebra/AlgebraTest.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.algebra 2 | 3 | 4 | import kotlinslang.control.failure 5 | import kotlinslang.control.optionOf 6 | import kotlinslang.control.success 7 | import kotlinslang.foldLeft 8 | import kotlinslang.foldLeftMap 9 | import kotlinslang.foldMap 10 | import kotlinslang.foldRight 11 | import kotlinslang.foldRightMap 12 | import kotlinslang.function.identity 13 | import org.assertj.core.api.Assertions.assertThat 14 | import org.junit.Ignore 15 | import org.junit.Test 16 | import java.util.NoSuchElementException 17 | 18 | /** 19 | * Series Test for Algebra Interfaces such as Functor, Monad and Monoid. 20 | * 21 | * @author Deny Prasetyo. 22 | */ 23 | 24 | class AlgebraTest { 25 | 26 | @Test 27 | fun shouldCombineMonoids() { 28 | val endo: Monoid<(Int) -> Int> = endoMonoid() 29 | val after = { i: Int -> i + 1 } 30 | val before = { i: Int -> i * 2 } 31 | assertThat(endo.combine(after, before)(2)).isEqualTo(5) 32 | } 33 | 34 | @Test 35 | fun monoidOfShouldCreate() { 36 | val monoid = monoidOf(zero = 10, combiner = { a: Int, b: Int -> a * b }) 37 | assertThat(monoid.zero()).isEqualTo(10) 38 | assertThat(monoid.combine(2, 4)).isEqualTo(8) 39 | } 40 | 41 | @Test 42 | fun monadLiftShouldCreateSome() { 43 | val liftedMonad = monadLift { l -> l + 4 * 0.1 } 44 | val monadOption = optionOf(20) 45 | val liftedOption = liftedMonad(monadOption) 46 | liftedOption.forEach { o -> assertThat(o).isEqualTo(20.4) }; 47 | } 48 | 49 | @Test 50 | fun monadLiftShouldCreateNone() { 51 | val liftedMonad = monadLift { l -> l + 4 * 0.1 } 52 | val liftedOption = liftedMonad(optionOf(null)) 53 | liftedOption.forEach { o -> assertThat(o).isNull() }; 54 | } 55 | 56 | @Test 57 | fun foldLeftBehaveCorrectly() { 58 | val zero = "zero" 59 | val value = "hello" 60 | val expected = "zero-hello" 61 | val combiner = { a: String, b: String -> "$a-$b" } 62 | val monoid = monoidOf(zero, combiner) 63 | 64 | val foldable = optionOf(value) 65 | assertThat(foldable.foldLeft(monoid)).isEqualTo(expected) 66 | assertThat(foldable.fold(zero, combiner)).isEqualTo(expected) 67 | 68 | val emptyFoldable = optionOf(null) 69 | assertThat(emptyFoldable.foldLeft(monoid)).isEqualTo(zero) 70 | assertThat(emptyFoldable.fold(zero, combiner)).isEqualTo(zero) 71 | } 72 | 73 | @Test 74 | fun foldRightBehaveCorrectly() { 75 | val zero = "zero" 76 | val value = "hello" 77 | val expected = "hello-zero" 78 | val combiner = { a: String, b: String -> "$a-$b" } 79 | val monoid = monoidOf(zero, combiner) 80 | 81 | val foldable = optionOf(value) 82 | assertThat(foldable.foldRight(zero, combiner)).isEqualTo(expected) 83 | assertThat(foldable.foldRight(monoid)).isEqualTo(expected) 84 | 85 | val emptyFoldable = optionOf(null) 86 | assertThat(emptyFoldable.foldRight(zero, combiner)).isEqualTo(zero) 87 | assertThat(emptyFoldable.foldRight(monoid)).isEqualTo(zero) 88 | 89 | } 90 | 91 | @Test 92 | fun foldLeftMapBehaveCorrectly() { 93 | val zero = "zero" 94 | val value = 42 95 | val expected = "zero-[42]" 96 | val combiner = { a: String, b: String -> "$a-$b" } 97 | val monoid = monoidOf(zero, combiner) 98 | 99 | val foldable = optionOf(value) 100 | assertThat(foldable.foldMap(monoid, { i -> "[$i]" })).isEqualTo(expected) 101 | assertThat(foldable.foldLeftMap(monoid, { i -> "[$i]" })).isEqualTo(expected) 102 | } 103 | 104 | @Test 105 | fun foldRightMapBehaveCorrectly() { 106 | val zero = "zero" 107 | val value = 42 108 | val expected = "[42]-zero" 109 | val combiner = { a: String, b: String -> "$a-$b" } 110 | val monoid = monoidOf(zero, combiner) 111 | 112 | val foldable = optionOf(value) 113 | assertThat(foldable.foldRightMap(monoid, { i -> "[$i]" })).isEqualTo(expected) 114 | } 115 | 116 | @Test 117 | fun functorIdentityLaw() { 118 | val value = 42 119 | val functorSome = optionOf(value) 120 | val functorNone = optionOf(null) 121 | 122 | assertThat(functorSome.map(identity())).isEqualTo(functorSome) 123 | assertThat(functorNone.map(identity())).isEqualTo(functorNone) 124 | 125 | val functorSuccess = success(value) 126 | val functorFailure = failure(NoSuchElementException("Failure Functor")) 127 | 128 | assertThat(functorSuccess.map(identity())).isEqualTo(functorSuccess) 129 | assertThat(functorFailure.map(identity())).isEqualTo(functorFailure) 130 | 131 | } 132 | 133 | @Test 134 | @Ignore 135 | fun functorAssociativeLaw() { 136 | val value = 42 137 | val functorSome = optionOf(value) 138 | val functorNone = optionOf(null) 139 | 140 | 141 | val functorSuccess = success(value) 142 | val functorFailure = failure(NoSuchElementException("Failure Functor")) 143 | 144 | 145 | } 146 | 147 | } -------------------------------------------------------------------------------- /src/test/kotlin/kotlinslang/control/OptionTest.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import kotlinslang.collection.emptyIterator 4 | import kotlinslang.collection.iteratorOf 5 | import kotlinslang.orElse 6 | import kotlinslang.orElseGet 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.junit.Test 9 | import java.util.NoSuchElementException 10 | import kotlin.test.assertEquals 11 | import kotlin.test.assertFailsWith 12 | import kotlin.test.assertTrue 13 | 14 | /** 15 | * [TODO: Documentation] 16 | * 17 | * @author Deny Prasetyo. 18 | */ 19 | 20 | class OptionTest { 21 | 22 | @Test 23 | fun noneBehaveCorrectly() { 24 | val none = none() 25 | val nothing = none() 26 | val noneOption = optionOf(null) 27 | 28 | assertThat(none.equals(noneOption)).isTrue() 29 | assertThat(nothing.equals(nothing)).isTrue() 30 | assertThat(none.equals(42)).isFalse() 31 | assertThat(none.isEmpty()).isTrue() 32 | assertThat(none.isDefined()).isFalse() 33 | assertFailsWith( 34 | exceptionClass = NoSuchElementException::class, 35 | block = { none.get() } 36 | ) 37 | 38 | assertEquals(none, noneOption) 39 | assertEquals(none.hashCode(), noneOption.hashCode()) 40 | assertEquals(none.toString(), noneOption.toString()) 41 | assertEquals(None, None) 42 | 43 | val otherElement = 142 44 | assertEquals(none.orElse(otherElement), otherElement) 45 | assertEquals(none.orElseGet { otherElement }, otherElement) 46 | assertFailsWith( 47 | exceptionClass = NullPointerException::class, 48 | block = { none.orElseThrow { -> NullPointerException("NPE") } } 49 | ) 50 | 51 | assertEquals(none.filter { t -> true }, none) 52 | assertEquals(none.filter { t -> false }, none) 53 | none.peek { t -> assertTrue(false, "Must Not reached") } 54 | 55 | assertEquals(none.iterator(), emptyIterator()) 56 | 57 | assertEquals(none.map { t -> t + 1 }, none) 58 | 59 | assertEquals(none.flatMap { t -> some(t + 1) }, none) 60 | 61 | assertEquals(none.transform { null }, null) 62 | 63 | } 64 | 65 | @Test 66 | fun someBehaveCorrectly() { 67 | val element = 42 68 | val some = some(element) 69 | val someOption = optionOf(element) 70 | 71 | assertThat(some.equals(someOption)).isTrue() 72 | assertThat(some.isEmpty()).isFalse() 73 | assertThat(some.isDefined()).isTrue() 74 | 75 | assertEquals(some.get(), element) 76 | 77 | assertEquals(some, someOption) 78 | assertThat(some.equals(42)).isFalse() 79 | assertThat(some.equals(null)).isFalse() 80 | assertThat(some.equals(some(142))).isFalse() 81 | assertThat(some.equals(some(42.0))).isFalse() 82 | assertEquals(some.hashCode(), someOption.hashCode()) 83 | assertEquals(some.toString(), someOption.toString()) 84 | 85 | 86 | val otherElement = 142 87 | assertEquals(some.orElse(otherElement), element) 88 | assertEquals(some.orElseGet { otherElement }, element) 89 | assertEquals(some.orElseThrow { -> NullPointerException("NPE") }, element) 90 | 91 | assertEquals(some.filter { t -> true }, some) 92 | assertEquals(some.filter { t -> false }, none()) 93 | 94 | var reached = false 95 | some.peek { t -> reached = true } 96 | assertThat(reached).isTrue() 97 | 98 | assertEquals(some.iterator().next(), iteratorOf(element).next()) 99 | 100 | assertEquals(some.map { t -> t + 1 }, some(element + 1)) 101 | 102 | assertEquals(some.flatMap { t -> some(t + 1) }, some(element + 1)) 103 | assertEquals(some.flatMap { t -> listOf(t + 1) }, some(element + 1)) 104 | assertEquals(some.flatMap { t -> listOf() }, none()) 105 | 106 | val transformed = some.transform { listOf(it.get()) } 107 | assertThat(transformed).isInstanceOf(List::class.java) 108 | 109 | } 110 | } -------------------------------------------------------------------------------- /src/test/kotlin/kotlinslang/control/TryTest.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import kotlinslang.orElse 4 | import kotlinslang.orElseGet 5 | import org.assertj.core.api.Assertions.assertThat 6 | import org.junit.Test 7 | import java.util.NoSuchElementException 8 | import kotlin.test.assertEquals 9 | import kotlin.test.assertFailsWith 10 | import kotlin.test.assertNotEquals 11 | import kotlin.test.assertTrue 12 | 13 | /** 14 | * [TODO: Documentation] 15 | * 16 | * @author Deny Prasetyo. 17 | */ 18 | 19 | class TryTest { 20 | 21 | @Test 22 | fun failureBehaveCorrectly() { 23 | val exception = ArithmeticException("Arithmetic") 24 | val failure = failure(exception) 25 | val failureTry = tryOf { throw exception } 26 | 27 | assertThat(failure.equals(failure)).isTrue() 28 | assertThat(failure.equals(failureTry)).isTrue() 29 | assertNotEquals(failure, success(42)) 30 | assertThat(failure.equals(10)).isFalse() 31 | assertThat(failure.equals(null)).isFalse() 32 | assertThat(failure.equals(failure(NullPointerException("NPE")))) 33 | 34 | assertThat(failure.isEmpty()).isTrue() 35 | assertThat(failure.isDefined()).isFalse() 36 | assertThat(failure.isFailure()).isTrue() 37 | assertThat(failure.isSuccess()).isFalse() 38 | 39 | assertEquals(failure.hashCode(), failureTry.hashCode()) 40 | assertEquals(failure.toString(), failureTry.toString()) 41 | 42 | assertThat(failure.getCause()).isInstanceOf(ArithmeticException::class.java) 43 | assertFailsWith( 44 | exceptionClass = ArithmeticException::class, 45 | block = { failure.get() } 46 | ) 47 | 48 | } 49 | 50 | @Test 51 | fun successBehaveCorrectly() { 52 | val element = 46 53 | val success = success(element) 54 | val successTry = tryOf { element } 55 | 56 | assertThat(success.equals(success)).isTrue() 57 | assertThat(success.equals(successTry)).isTrue() 58 | assertNotEquals(success, failure(NullPointerException("NPE"))) 59 | assertThat(success.equals(10)).isFalse() 60 | assertThat(success.equals(success(142))).isFalse() 61 | assertThat(success.equals(null)).isFalse() 62 | assertThat(success.equals(success(element))) 63 | 64 | assertThat(success.isEmpty()).isFalse() 65 | assertThat(success.isDefined()).isTrue() 66 | assertThat(success.isFailure()).isFalse() 67 | assertThat(success.isSuccess()).isTrue() 68 | 69 | assertEquals(success.hashCode(), successTry.hashCode()) 70 | assertEquals(success.toString(), successTry.toString()) 71 | 72 | assertThat(success.get()).isEqualTo(element) 73 | assertFailsWith( 74 | exceptionClass = UnsupportedOperationException::class, 75 | block = { success.getCause() } 76 | ) 77 | } 78 | 79 | @Test 80 | fun failureOrElseBehaveCorrectly() { 81 | val elseElement = 42 82 | val exception = ArithmeticException("Arithmetic") 83 | val failure = failure(exception) 84 | 85 | assertEquals(failure.orElse(elseElement), elseElement) 86 | assertEquals(failure.orElseGet { -> elseElement }, elseElement) 87 | assertEquals(failure.orElseGet { t -> elseElement }, elseElement) 88 | assertFailsWith( 89 | exceptionClass = NullPointerException::class, 90 | block = { failure.orElseThrow { -> NullPointerException("NPE") } } 91 | ) 92 | assertFailsWith( 93 | exceptionClass = NullPointerException::class, 94 | block = { failure.orElseThrow { t -> NullPointerException("NPE") } } 95 | ) 96 | var reached = false 97 | failure.orElseRun { t -> reached = true } 98 | assertTrue(reached, "Or Else Run Must Invoked") 99 | 100 | } 101 | 102 | @Test 103 | fun successOrElseBehaveCorrectly() { 104 | val element = 42 105 | val elseElement = 142 106 | val success = success(element) 107 | 108 | assertEquals(success.orElse(elseElement), element) 109 | assertEquals(success.orElseGet { -> elseElement }, element) 110 | assertEquals(success.orElseGet { t -> elseElement }, element) 111 | assertEquals(success.orElseThrow { -> NullPointerException("NPE") }, element) 112 | assertEquals(success.orElseThrow { t -> NullPointerException("NPE") }, element) 113 | success.orElseRun { t -> assertTrue(false, "Must not Reached") } 114 | } 115 | 116 | @Test 117 | fun toEitherBehaveCorrectly() { 118 | val exception = ArithmeticException("Arithmetic") 119 | val failure = failure(exception) 120 | 121 | val element = 42 122 | val success = success(element) 123 | 124 | assertThat(failure.toEither()).isExactlyInstanceOf(Left::class.java) 125 | assertThat(success.toEither()).isExactlyInstanceOf(Right::class.java) 126 | 127 | } 128 | 129 | @Test 130 | fun andThenBehaveCorrectly() { 131 | val exception = ArithmeticException("Arithmetic") 132 | val failure = failure(exception) 133 | 134 | val element = 42 135 | val success = success(element) 136 | 137 | failure.andThen { t -> assertTrue(false, "Must not Reached") } 138 | failure.andThen { -> assertTrue(false, "Must not Reached") } 139 | 140 | var andThenReached = false 141 | success.andThen { t -> andThenReached = true } 142 | assertTrue(andThenReached) 143 | 144 | andThenReached = false 145 | success.andThen { -> andThenReached = true } 146 | assertTrue(andThenReached) 147 | } 148 | 149 | @Test 150 | fun failedBehaveCorrectly() { 151 | val exception = ArithmeticException("Arithmetic") 152 | val failure = failure(exception) 153 | 154 | val element = 42 155 | val success = success(element) 156 | 157 | assertThat(failure.failed()).isInstanceOf(Success::class.java) 158 | assertThat(success.failed()).isInstanceOf(Failure::class.java) 159 | 160 | } 161 | 162 | @Test 163 | fun filterBehaveCorrectly() { 164 | val exception = ArithmeticException("Arithmetic") 165 | val failure = failure(exception) 166 | 167 | val element = 42 168 | val success = success(element) 169 | 170 | assertEquals(failure.filter { t -> true }, failure) 171 | assertEquals(success.filter { t -> true }, success) 172 | 173 | assertEquals(failure.filter { t -> false }, failure) 174 | assertThat(success.filter { t -> false }).isInstanceOf(Failure::class.java) 175 | 176 | assertFailsWith( 177 | exceptionClass = NoSuchElementException::class, 178 | block = { success.filter { t -> false }.get() } 179 | ) 180 | 181 | assertThat(success.filter { t -> throw IllegalArgumentException("IAE") }) 182 | .isInstanceOf(Failure::class.java) 183 | 184 | assertFailsWith( 185 | exceptionClass = IllegalArgumentException::class, 186 | block = { success.filter { t -> throw IllegalArgumentException("IAE") }.get() } 187 | ) 188 | 189 | } 190 | 191 | @Test 192 | fun peekBehaveCorrectly() { 193 | val exception = ArithmeticException("Arithmetic") 194 | val failure = failure(exception) 195 | 196 | val element = 42 197 | val success = success(element) 198 | 199 | assertThat(failure.peek { t -> assertTrue(false, "Must not Reached") }).isEqualTo(failure) 200 | 201 | var reached = false 202 | assertThat(success.peek { t -> reached = true }).isEqualTo(success) 203 | assertTrue(reached) 204 | 205 | } 206 | 207 | } -------------------------------------------------------------------------------- /src/test/kotlin/kotlinslang/control/UtilTest.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.control 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.Test 5 | import java.util.NoSuchElementException 6 | import java.util.Optional 7 | import kotlin.test.assertEquals 8 | import kotlin.test.assertFailsWith 9 | 10 | /** 11 | * [TODO: Documentation] 12 | * 13 | * @author Deny Prasetyo. 14 | */ 15 | 16 | class UtilTest { 17 | 18 | @Test 19 | fun toOptionForNullableShouldSuccess() { 20 | val nullable: Int? = null 21 | val none = nullable.toOption() 22 | assertThat(none.isEmpty()).isTrue() 23 | assertEquals(none, none()) 24 | 25 | assertFailsWith( 26 | exceptionClass = NoSuchElementException::class, 27 | block = { none.get() }, 28 | message = "Call get() on None Throws NSEE" 29 | ) 30 | 31 | val notNull: Int? = 90 32 | val some = notNull.toOption() 33 | assertThat(some.isEmpty()).isFalse() 34 | assertThat(some.get()).isEqualTo(notNull) 35 | 36 | } 37 | 38 | @Test 39 | fun noneMustProduceNoneOption() { 40 | val none = none() 41 | assertThat(none.isEmpty()).isTrue() 42 | assertThat(none.isDefined()).isFalse() 43 | assertFailsWith( 44 | exceptionClass = NoSuchElementException::class, 45 | block = { none.get() }, 46 | message = "Call get() on None Throws NSEE" 47 | ) 48 | } 49 | 50 | @Test 51 | fun someMustProduceSomeOption() { 52 | val element = 10 53 | val some = some(element) 54 | assertThat(some.isEmpty()).isFalse() 55 | assertThat(some.isDefined()).isTrue() 56 | assertThat(some.get()).isEqualTo(element) 57 | } 58 | 59 | @Test 60 | fun optionWhenProductCorrectOptionBasedOnCondition() { 61 | val element = 42 62 | val none = optionWhen(condition = false, supplier = { element }) 63 | assertThat(none.isEmpty()).isTrue() 64 | assertThat(none.isDefined()).isFalse() 65 | assertFailsWith( 66 | exceptionClass = NoSuchElementException::class, 67 | block = { none.get() }, 68 | message = "Call get() on None Throws NSEE" 69 | ) 70 | 71 | val some = optionWhen(condition = true, supplier = { element }) 72 | assertThat(some.isEmpty()).isFalse() 73 | assertThat(some.isDefined()).isTrue() 74 | assertThat(some.get()).isEqualTo(element) 75 | } 76 | 77 | @Test 78 | fun toOptionFromJavaOptionalShouldSuccess() { 79 | val nullable: Int? = null 80 | val none = Optional.ofNullable(nullable).toOption() 81 | assertThat(none.isEmpty()).isTrue() 82 | assertEquals(none, none()) 83 | 84 | assertFailsWith( 85 | exceptionClass = NoSuchElementException::class, 86 | block = { none.get() }, 87 | message = "Call get() on None Throws NSEE" 88 | ) 89 | 90 | val notNull: Int? = 90 91 | val some = Optional.ofNullable(notNull).toOption() 92 | assertThat(some.isEmpty()).isFalse() 93 | assertThat(some.get()).isEqualTo(notNull) 94 | } 95 | 96 | 97 | @Test 98 | fun rightMustProduceCorrectEither() { 99 | val value = 42 100 | val rightEither = right(value) 101 | assertThat(rightEither.right().isDefined()).isTrue() 102 | assertThat(rightEither.left().isEmpty()).isTrue() 103 | assertThat(rightEither.right().get()).isEqualTo(value) 104 | assertFailsWith( 105 | exceptionClass = NoSuchElementException::class, 106 | block = { rightEither.left().get() } 107 | ) 108 | } 109 | 110 | @Test 111 | fun leftMustProduceCorrectEither() { 112 | val value = 42 113 | val leftEither = left(value) 114 | assertThat(leftEither.left().isDefined()).isTrue() 115 | assertThat(leftEither.right().isEmpty()).isTrue() 116 | assertThat(leftEither.left().get()).isEqualTo(value) 117 | assertFailsWith( 118 | exceptionClass = NoSuchElementException::class, 119 | block = { leftEither.right().get() } 120 | ) 121 | } 122 | 123 | @Test 124 | fun successMustProduceCorrectTry() { 125 | val value = 42 126 | val successTry = success(value) 127 | assertThat(successTry.isDefined()).isTrue() 128 | assertThat(successTry.isSuccess()).isTrue() 129 | assertThat(successTry.isEmpty()).isFalse() 130 | assertThat(successTry.isFailure()).isFalse() 131 | assertThat(successTry.get()).isEqualTo(value) 132 | assertFailsWith( 133 | exceptionClass = UnsupportedOperationException::class, 134 | block = { successTry.getCause() } 135 | ) 136 | } 137 | 138 | @Test 139 | fun failureMustProduceCorrectTry() { 140 | val cause = IllegalArgumentException("Failure With Illegal Argument") 141 | val failureTry = failure(cause) 142 | assertThat(failureTry.isDefined()).isFalse() 143 | assertThat(failureTry.isEmpty()).isTrue() 144 | assertThat(failureTry.isSuccess()).isFalse() 145 | assertThat(failureTry.isFailure()).isTrue() 146 | assertThat(failureTry.getCause()).isEqualTo(cause) 147 | assertFailsWith( 148 | exceptionClass = IllegalArgumentException::class, 149 | block = { failureTry.get() } 150 | ) 151 | } 152 | 153 | @Test 154 | fun tryRunMustProduceCorrectTry() { 155 | val successTry = tryRun { println("Something not Throw Exception") } 156 | assertThat(successTry.isDefined()).isTrue() 157 | assertThat(successTry.isSuccess()).isTrue() 158 | assertThat(successTry.isEmpty()).isFalse() 159 | assertThat(successTry.isFailure()).isFalse() 160 | assertThat(successTry.get()).isEqualTo(Unit) 161 | assertFailsWith( 162 | exceptionClass = UnsupportedOperationException::class, 163 | block = { successTry.getCause() } 164 | ) 165 | 166 | @Suppress("DIVISION_BY_ZERO") 167 | val failedTry = tryRun { println("Something Throw Exception ${3 / 0}") } 168 | assertThat(failedTry.isDefined()).isFalse() 169 | assertThat(failedTry.isSuccess()).isFalse() 170 | assertThat(failedTry.isEmpty()).isTrue() 171 | assertThat(failedTry.isFailure()).isTrue() 172 | assertThat(failedTry.getCause()).isInstanceOf(Throwable::class.java) 173 | assertThat(failedTry.getCause()).isExactlyInstanceOf(ArithmeticException::class.java) 174 | assertFailsWith( 175 | exceptionClass = ArithmeticException::class, 176 | block = { failedTry.get() } 177 | ) 178 | 179 | } 180 | 181 | @Test 182 | fun tryOfMustProduceCorrectTry() { 183 | val successTry = tryOf { println("Something not Throw Exception") } 184 | assertThat(successTry.isDefined()).isTrue() 185 | assertThat(successTry.isSuccess()).isTrue() 186 | assertThat(successTry.isEmpty()).isFalse() 187 | assertThat(successTry.isFailure()).isFalse() 188 | assertThat(successTry.get()).isEqualTo(Unit) 189 | assertFailsWith( 190 | exceptionClass = UnsupportedOperationException::class, 191 | block = { successTry.getCause() } 192 | ) 193 | 194 | @Suppress("DIVISION_BY_ZERO") 195 | val failedTry = tryOf { println("Something Throw Exception ${3 / 0}") } 196 | assertThat(failedTry.isDefined()).isFalse() 197 | assertThat(failedTry.isSuccess()).isFalse() 198 | assertThat(failedTry.isEmpty()).isTrue() 199 | assertThat(failedTry.isFailure()).isTrue() 200 | assertThat(failedTry.getCause()).isInstanceOf(Throwable::class.java) 201 | assertThat(failedTry.getCause()).isExactlyInstanceOf(ArithmeticException::class.java) 202 | assertFailsWith( 203 | exceptionClass = ArithmeticException::class, 204 | block = { failedTry.get() } 205 | ) 206 | 207 | val value = 42 208 | val successTryOf = tryOf { value * 1 } 209 | assertThat(successTryOf.isDefined()).isTrue() 210 | assertThat(successTryOf.isSuccess()).isTrue() 211 | assertThat(successTryOf.isEmpty()).isFalse() 212 | assertThat(successTryOf.isFailure()).isFalse() 213 | assertThat(successTryOf.get()).isEqualTo(value) 214 | assertFailsWith( 215 | exceptionClass = UnsupportedOperationException::class, 216 | block = { successTryOf.getCause() } 217 | ) 218 | 219 | @Suppress("DIVISION_BY_ZERO") 220 | val failedTryOf = tryOf { value * (3 / 0) } 221 | assertThat(failedTryOf.isDefined()).isFalse() 222 | assertThat(failedTryOf.isSuccess()).isFalse() 223 | assertThat(failedTryOf.isEmpty()).isTrue() 224 | assertThat(failedTryOf.isFailure()).isTrue() 225 | assertThat(failedTryOf.getCause()).isInstanceOf(Throwable::class.java) 226 | assertThat(failedTryOf.getCause()).isExactlyInstanceOf(ArithmeticException::class.java) 227 | assertFailsWith( 228 | exceptionClass = ArithmeticException::class, 229 | block = { failedTryOf.get() } 230 | ) 231 | 232 | } 233 | 234 | } -------------------------------------------------------------------------------- /src/test/kotlin/kotlinslang/function/FunctionExtTest.kt: -------------------------------------------------------------------------------- 1 | package kotlinslang.function 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.Test 5 | import kotlin.test.assertFailsWith 6 | 7 | 8 | /** 9 | * Test for FunctionExt 10 | * 11 | * @author Deny Prasetyo. 12 | */ 13 | 14 | class FunctionExtTest { 15 | 16 | data class RawMaterial(val id: Int, val description: String, val price: Double) 17 | data class RawProduct(val id: Int, val description: String, val price: Double) 18 | data class Product(val id: Int, val description: String, val price: Double) 19 | 20 | @Test 21 | fun composeExecuteParamCorrectly() { 22 | val before = { d: RawMaterial -> RawProduct(d.id, "Raw Product from Material of " + d.description, (d.price * 1.2)) } 23 | val funcOne = { d: RawProduct -> Product(d.id, "Product from " + d.description, (d.price * 1.4)) } 24 | val composedFunction = funcOne.compose(before) 25 | 26 | val rawMaterial = RawMaterial(42, "T Shirt", 1000.0) 27 | val product = composedFunction(rawMaterial) 28 | 29 | assertThat(product.id).isEqualTo(rawMaterial.id) 30 | assertThat(product.price).isEqualTo(rawMaterial.price * 1.2 * 1.4) 31 | assertThat(product.description).startsWith("Product from ").contains("Raw Product").contains(rawMaterial.description) 32 | } 33 | 34 | @Test 35 | fun composeWithExceptionWilThrowException() { 36 | val before = { d: Int -> d / 0 } 37 | val funcOne = { d: Int -> "String Value of Integer => $d" } 38 | val composedFunction = funcOne.compose(before) 39 | 40 | val param = 42 41 | 42 | assertFailsWith( 43 | exceptionClass = ArithmeticException::class, 44 | block = { composedFunction(param) }, 45 | message = "Must Throws Exception" 46 | ) 47 | } 48 | 49 | 50 | } --------------------------------------------------------------------------------

12 | * Option is a monadic container type which 13 | * represents an optional value. Instances of Option are either an instance of {@link javaslang.control.Some} or the 14 | * singleton {@link javaslang.control.None}. 15 | *