From 9ca5ce35e7f83bbedfd707e8d67f1c7fc477ffdf Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 23 Oct 2016 12:47:20 +0200 Subject: [PATCH 001/173] Update to gradle 3.4 --- gradle/wrapper/gradle-wrapper.jar | Bin 53637 -> 54208 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 68 +++++++++++++---------- gradlew.bat | 14 ++--- props-core-scalacheck/build.gradle | 10 +--- 5 files changed, 48 insertions(+), 48 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 05ef575b0cd0173fc735f2857ce4bd594ce4f6bd..7f9251933fd4846b7f2c971bb2c5f6a575ad3e3a 100644 GIT binary patch delta 24962 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 F{|CS?UfTcw delta 24405 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 23175860..aa8a01af 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Dec 06 21:53:19 EST 2016 +#Mon Feb 27 21:23:04 CET 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4-bin.zip diff --git a/gradlew b/gradlew index 9d82f789..4453ccea 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,12 +6,30 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,26 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -150,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index aec99730..e95643d6 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/props-core-scalacheck/build.gradle b/props-core-scalacheck/build.gradle index 4d1a04e9..02f3652a 100644 --- a/props-core-scalacheck/build.gradle +++ b/props-core-scalacheck/build.gradle @@ -4,16 +4,12 @@ archivesBaseName = "${project.projectName}-${project.name}" apply plugin: 'scala' ext { - scalaVersion = "2.11.7" + scalaVersion = "2.11.8" scalacheckScalaVersion = "2.11" - scalacheckVersion = "1.12.4" + scalacheckVersion = "1.12.5" signModule = true } -tasks.withType(ScalaCompile) { - scalaCompileOptions.useAnt = false -} - dependencies { compile project(":core") compile "org.scala-lang:scala-library:$scalaVersion" @@ -22,8 +18,6 @@ dependencies { testCompile dependencyJunit } -sourceCompatibility = "1.7" - performSigning(signingEnabled, signModule) configureUpload(signingEnabled, signModule) From 6fa8bce2008c73d5995af20f6f41f60908e2ca6d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 23 Oct 2016 12:49:34 +0200 Subject: [PATCH 002/173] add "_1.8" suffix to artifact cross-built for java 8. Use JDK7 as retrolambda target --- .travis.yml | 1 - build.gradle | 8 ++++---- java-core/build.gradle | 2 ++ java8/build.gradle | 4 +++- lib.gradle | 2 ++ quickcheck/build.gradle | 4 ++-- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4a7c9290..8f3091d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,6 @@ before_script: - sudo service x11-common stop || true script: - - jdk_switcher use openjdk6 && export JAVA6_HOME=$JAVA_HOME - jdk_switcher use oraclejdk7 && export JAVA7_HOME=$JAVA_HOME - jdk_switcher use oraclejdk8 && export JAVA8_HOME=$JAVA_HOME - ./gradlew build coverage -s -i diff --git a/build.gradle b/build.gradle index 1ccd524a..5bedc102 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ ext { buildscript { ext { - uptodateVersion = "1.6.0" - retrolambdaPluginVersion = "3.2.5" + uptodateVersion = "1.6.2" + retrolambdaPluginVersion = "3.3.1" retrolambdaVersion = "2.3.0" } @@ -77,8 +77,8 @@ allprojects { displayCompilerWarnings = true newJdkEnvVar = "JAVA8_HOME" - oldJdkEnvVar = "JAVA6_HOME" - retroLambdaTarget = JavaVersion.VERSION_1_6 + oldJdkEnvVar = "JAVA7_HOME" + retroLambdaTarget = JavaVersion.VERSION_1_7 } repositories { diff --git a/java-core/build.gradle b/java-core/build.gradle index 88e9a25f..6a92cc28 100644 --- a/java-core/build.gradle +++ b/java-core/build.gradle @@ -1,5 +1,6 @@ archivesBaseName = "${project.projectName}-${project.name}" +configureAllRetroLambda() ext { signModule = true @@ -13,4 +14,5 @@ dependencies { performSigning(signingEnabled, signModule) configureUpload(signingEnabled, signModule) + uploadArchives.enabled = true diff --git a/java8/build.gradle b/java8/build.gradle index 88e9a25f..d6d4c485 100644 --- a/java8/build.gradle +++ b/java8/build.gradle @@ -13,4 +13,6 @@ dependencies { performSigning(signingEnabled, signModule) configureUpload(signingEnabled, signModule) -uploadArchives.enabled = true +if (!useRetroLambda) { + uploadArchives.enabled = true +} diff --git a/lib.gradle b/lib.gradle index 28be5e34..253ed251 100644 --- a/lib.gradle +++ b/lib.gradle @@ -94,6 +94,8 @@ void configureRetroLambda(boolean useRetroLambda, String newJdkEnvVar, String ol dependencies { retrolambdaConfig "net.orfjackal.retrolambda:retrolambda:$retrolambdaVersion" } + } else { + project.archivesBaseName = "${project.archivesBaseName}_1.8" } } diff --git a/quickcheck/build.gradle b/quickcheck/build.gradle index 4d3a25be..45e379da 100644 --- a/quickcheck/build.gradle +++ b/quickcheck/build.gradle @@ -3,10 +3,10 @@ ext { signModule = true } -configureAllRetroLambda() - archivesBaseName = "${project.projectName}-${project.name}" +configureAllRetroLambda() + dependencies { compile project(":core") compile dependencyJunit From 3dcdf401182899611608537dd9862f06eed18062 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Mon, 27 Feb 2017 21:18:51 +0100 Subject: [PATCH 003/173] java-core module should be publish with retrolambda processing. --- java-core/build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java-core/build.gradle b/java-core/build.gradle index 6a92cc28..032c2ca5 100644 --- a/java-core/build.gradle +++ b/java-core/build.gradle @@ -16,3 +16,5 @@ configureUpload(signingEnabled, signModule) uploadArchives.enabled = true + +configureAllRetroLambda() \ No newline at end of file From d1b15e16f8287b1265efc6516801aba29abdd290 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Mon, 27 Feb 2017 21:25:41 +0100 Subject: [PATCH 004/173] upgrade to latest retrolambda version. --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 5bedc102..b9597063 100644 --- a/build.gradle +++ b/build.gradle @@ -7,8 +7,8 @@ ext { buildscript { ext { uptodateVersion = "1.6.2" - retrolambdaPluginVersion = "3.3.1" - retrolambdaVersion = "2.3.0" + retrolambdaPluginVersion = "3.5.0" + retrolambdaVersion = "2.5.1" } repositories { From 0759750518cfbd920170d75c73c199d80b5bb49e Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 17 Mar 2017 00:43:54 +1000 Subject: [PATCH 005/173] Added release notes for 4.7 --- etc/release-notes/release-notes-4.7.adoc | 44 ++++++++++++++++++++---- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/etc/release-notes/release-notes-4.7.adoc b/etc/release-notes/release-notes-4.7.adoc index 3c1b265f..6f5958de 100644 --- a/etc/release-notes/release-notes-4.7.adoc +++ b/etc/release-notes/release-notes-4.7.adoc @@ -1,28 +1,58 @@ = Release 4.7 -Proposed release: September 2016 +Released: unknown, potentially 25 March 2017 == Enhancements -* TODO. +* Added Hash Array Mapped Trie (#284). +* Improve performance of List#groupBy via a mutable buffer (#288). +* Avoid unnecessary wrapping of F0 into P1.lazy where applicable. Use static P factories for this (#284). +* Added semigroup constraint to List.traverseValidation (#287). +* Added first class fold for either (catamorphism) (#284). +* Added first class fold for Option (catamorphism) (#284). +* Added optimisable definitions for Equal (#284). +* Added uncurried version of foldRight, foldLeft and append to NonEmptyList (#284). +* Added optimisable definitions for Monoid and Semigroup (#284). +* Improved performance of List.minimum and maximum by using uncurried Ord.max and Ord.min (#284). +* Removed deprecation of Monoid, Ord and Semigroup constructors for non Java 8 versions (#284). +* Added safe List minimumOption and maximumOption and NonEmptyList minimum and maximum. +* Added Set.lookup, Set.lookupLT, Set.lookupGT, Set.lookupLE and Set.lookupGE from Haskell (#305). == Fixes -* TODO. +* Make Stream conversions respect laziness (#284). +* Fixed Ord#isGreaterThan(A)/isLesserThan(A) not using strict inequality (#293). +* Correctly convert Option.some(null) to Optional (#284). +* Fixed override in P.p(A) (#284). +* Avoid unnecessary currying in uncurried version of foldRight (#284). == Internal -* TODO. +* Deprecated unsafe Ord instance and List#groupBy without an Ord parameter (#290). +* Added tests for Strings class (#295). +* Use more method references (#284). +* Converted memoisation tests from JUnit to PropertyTestRunner (#284) for hard memoisation. +* Improved implementation of P1#_1 method (#284). +* Make use of weak memoisation explicit (#284). +* Enable backport of default methods using Retro Lambda (#284). +* Upgrade to Gradle 2.14.1 (#310). +* Switch from Coveralls to cover.io (#308). +* Force strict evaluation in TreeMap.map (#309). == Breaking Changes -* TODO. +* none. == Documentation -* TODO. +* none. == Contributors -* TODO. +* Jean-Baptiste Giradeau +* Mark Perry +* Gabor Liptak +* Shimi Bandiel +* Siro Mateos + From 1faf5b9184d82cca5929747f5cfb4e67100752ab Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sat, 25 Mar 2017 18:12:58 +0100 Subject: [PATCH 006/173] Make retrolambda target java 6. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b9597063..6c417b5c 100644 --- a/build.gradle +++ b/build.gradle @@ -78,7 +78,7 @@ allprojects { newJdkEnvVar = "JAVA8_HOME" oldJdkEnvVar = "JAVA7_HOME" - retroLambdaTarget = JavaVersion.VERSION_1_7 + retroLambdaTarget = JavaVersion.VERSION_1_6 } repositories { From 4d36fcddcf9899191899d83ec80546e41c528bac Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Mon, 27 Mar 2017 20:58:13 +0200 Subject: [PATCH 007/173] Update gradle wrapper to 3.4.1 --- gradle/wrapper/gradle-wrapper.jar | Bin 54208 -> 54208 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f9251933fd4846b7f2c971bb2c5f6a575ad3e3a..2dcd00d1ccd90b5b3d39e0da139893d5e7651ae4 100644 GIT binary patch delta 957 zcmYL{YeR2CHVzW+1q!^`jYJm-H7=RM~PJGfy7Cok7> zNeV)wQqo{Ew8@oH9vWZXgMDqKMn%VL(_E8?{Lh&9j3b{D5MBlRSSP@x63G?|LQ7=R zXym1+B#^RI@V5MX4V^6my6OdBhR2kT7)&kk9S8Qi$YUoQ2bx0 zV6pcgc*9!(zV=$cO(RyYd!z~!`6|IGAIm@X8BxppIw11RKJ7@MYKvt{go9-WoN4UqYjOBjCPNKHP86$Bs>NR6*{@nz*arSU)$7?a}Pt?i@jg-GC Ljc`HKKC%BF8^&kK delta 957 zcmYL{T}YEr7{|}{s#}wpQ-d_jR9Fd>C1N%Epig46HJg#8PIG48WDCbiFA}1gE>cfK z(WF3YBVkdCA`!H0x^Fj?KB-8hM7l}JEKurw|7Y06%kTF*=YJ08J?He>xPBWa*J-%8 zG(x0OB5TfVlPje>)UW4SX4;C&)6##KrcEO9KQrHy+h)t=1O!(Bzt#zG=80sB1ffN; zNiuISagU*j+I6ZxPFoDWlT5kMd z1vR)-*$Mn5OQaJY9$!r*gedq=rxA(M$ima0FI7N*TISV)6<#%H_m+UOUJbb0cM$CG zvHb5C^?t2zfV9i60>}IYa7Tc#Gf;|pHgFz19n^y_gGO*==oolCR0e(x)r0%Pr@-DY zo1Z*%0(H|+9XLM3a+=5?)b}GS_a|~3wK2*Vj#i;wGtB1Sijf;(54U!t7USMntsGTL O`KwY37ep Date: Mon, 27 Mar 2017 20:58:59 +0200 Subject: [PATCH 008/173] java-core build: remove duplicate call of configureAllRetroLambda() --- java-core/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/java-core/build.gradle b/java-core/build.gradle index 032c2ca5..3b636072 100644 --- a/java-core/build.gradle +++ b/java-core/build.gradle @@ -1,6 +1,5 @@ archivesBaseName = "${project.projectName}-${project.name}" -configureAllRetroLambda() ext { signModule = true From 8fb59b9f1ed3771296c4a164e5cde4439a4e82c4 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Mon, 27 Mar 2017 21:17:53 +0200 Subject: [PATCH 009/173] 4.7 release. --- README.adoc | 22 +++++++++++----------- build.gradle | 2 +- etc/release-notes/release-notes-4.7.adoc | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.adoc b/README.adoc index 418605de..6c5d3c1e 100644 --- a/README.adoc +++ b/README.adoc @@ -31,16 +31,16 @@ The recommended way to download and use the project is through your build tool. The Functional Java artifact is published to Maven Central using the group `org.functionaljava` with three published artifacts: -* the core library (`functionaljava`) +* the core library (`functionaljava` or `functionaljava_1.8` if you use Java 8+) * Java 8 specific support (`functionaljava-java8`) -* property based testing (`functionaljava-quickcheck`) +* property based testing (`functionaljava-quickcheck` or `functionaljava-quickcheck_1.8` if you use Java 8+) -The latest stable version is `4.6`. This can be added to your Gradle project by adding the dependencies: +The latest stable version is `4.7`. This can be added to your Gradle project by adding the dependencies: ---- -compile "org.functionaljava:functionaljava:4.6" -compile "org.functionaljava:functionaljava-java8:4.6" -compile "org.functionaljava:functionaljava-quickcheck:4.6" -compile "org.functionaljava:functionaljava-java-core:4.6" +compile "org.functionaljava:functionaljava:4.7" +compile "org.functionaljava:functionaljava-java8:4.7" +compile "org.functionaljava:functionaljava-quickcheck:4.7" +compile "org.functionaljava:functionaljava-java-core:4.7" ---- and in Maven: @@ -48,22 +48,22 @@ and in Maven: org.functionaljava functionaljava - 4.6 + 4.7 org.functionaljava functionaljava-java8 - 4.6 + 4.7 org.functionaljava functionaljava-quickcheck - 4.6 + 4.7 org.functionaljava functionaljava-java-core - 4.6 + 4.7 ---- diff --git a/build.gradle b/build.gradle index 6c417b5c..c0152d08 100644 --- a/build.gradle +++ b/build.gradle @@ -46,7 +46,7 @@ allprojects { defaultTasks "build" ext { - isSnapshot = true + isSnapshot = false fjBaseVersion = "4.7" snapshotAppendix = "-SNAPSHOT" diff --git a/etc/release-notes/release-notes-4.7.adoc b/etc/release-notes/release-notes-4.7.adoc index 6f5958de..428a0726 100644 --- a/etc/release-notes/release-notes-4.7.adoc +++ b/etc/release-notes/release-notes-4.7.adoc @@ -1,7 +1,7 @@ = Release 4.7 -Released: unknown, potentially 25 March 2017 +Released: 27 March 2017 == Enhancements From 0ff3a9c27425736994f6f336aec68e40b6183884 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Mon, 27 Mar 2017 21:46:59 +0200 Subject: [PATCH 010/173] Prepare next iteration. --- build.gradle | 6 ++--- etc/release-notes/release-notes-4.8.adoc | 28 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 etc/release-notes/release-notes-4.8.adoc diff --git a/build.gradle b/build.gradle index c0152d08..04209664 100644 --- a/build.gradle +++ b/build.gradle @@ -46,12 +46,12 @@ allprojects { defaultTasks "build" ext { - isSnapshot = false - fjBaseVersion = "4.7" + isSnapshot = true + fjBaseVersion = "4.8" snapshotAppendix = "-SNAPSHOT" fjVersion = fjBaseVersion + (isSnapshot ? snapshotAppendix : "") - fjConsumeVersion = "4.6" + fjConsumeVersion = "4.7" signModule = false useRetroLambda = false diff --git a/etc/release-notes/release-notes-4.8.adoc b/etc/release-notes/release-notes-4.8.adoc new file mode 100644 index 00000000..22dbc552 --- /dev/null +++ b/etc/release-notes/release-notes-4.8.adoc @@ -0,0 +1,28 @@ + += Release + +Proposed release: + +== Enhancements + +* TODO. + +== Fixes + +* TODO. + +== Internal + +* TODO. + +== Breaking Changes + +* TODO. + +== Documentation + +* TODO. + +== Contributors + +* TODO. From 07f94fa172d9465de69abea188ecbdbc73c09078 Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Mon, 1 May 2017 21:44:09 -0500 Subject: [PATCH 011/173] Fixed a bug in the NonEmptyList Semigroup implementation that resulted in the same NonEmptyList appended to itself. --- core/src/main/java/fj/Semigroup.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index 74d7de9b..16b0d130 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -534,7 +534,7 @@ public static Semigroup> nonEmptyListSemigroup() { return semigroupDef(new Definition>() { @Override public NonEmptyList append(NonEmptyList a1, NonEmptyList a2) { - return a1.append(a1); + return a1.append(a2); } @Override From 405c3ec31351bf8518bfec8b91bf8be66056991b Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Mon, 1 May 2017 21:44:46 -0500 Subject: [PATCH 012/173] Added Scalacheck Arbitrary implementations for Natural and NonEmptyList. --- .../main/scala/fj/data/ArbitraryNatural.scala | 13 +++++++++++++ .../scala/fj/data/ArbitraryNonEmptyList.scala | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 props-core-scalacheck/src/main/scala/fj/data/ArbitraryNatural.scala create mode 100644 props-core-scalacheck/src/main/scala/fj/data/ArbitraryNonEmptyList.scala diff --git a/props-core-scalacheck/src/main/scala/fj/data/ArbitraryNatural.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryNatural.scala new file mode 100644 index 00000000..4210eef7 --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryNatural.scala @@ -0,0 +1,13 @@ +package fj.data + +import org.scalacheck.Arbitrary + +/** + * A Scalacheck [[Arbitrary]] for [[Natural]]. + */ +object ArbitraryNatural { + + implicit def arbitraryNatural: Arbitrary[Natural] = + Arbitrary(Arbitrary.arbBigInt.arbitrary.map(_.abs).map(bi => Natural.natural(bi.bigInteger).some())) + +} diff --git a/props-core-scalacheck/src/main/scala/fj/data/ArbitraryNonEmptyList.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryNonEmptyList.scala new file mode 100644 index 00000000..2258ba9b --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryNonEmptyList.scala @@ -0,0 +1,16 @@ +package fj.data + +import fj.data.NonEmptyList.nel +import org.scalacheck.Arbitrary.arbitrary +import org.scalacheck.{Arbitrary, Gen} + +/** + * A Scalacheck [[Arbitrary]] for [[NonEmptyList]]. + */ +object ArbitraryNonEmptyList { + implicit def arbitraryNonEmptyList[A](implicit a: Arbitrary[A]): Arbitrary[NonEmptyList[A]] = + Arbitrary(nelOf(arbitrary[A])) + + def nelOf[A](g: => Gen[A]): Gen[NonEmptyList[A]] = + Gen.nonEmptyListOf(g).map(l => l.tail.foldRight(nel(l.head))((x, n) => n.cons(x))) +} From ef811305b0e60e8b4dd5e42c601c8b3cbfed1fe7 Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Tue, 2 May 2017 00:15:25 -0500 Subject: [PATCH 013/173] Added unit test coverage for Semigroup implementations. The StringBuffer and StringBuilder tests fail because both of those types are mutable. The IO test fails because the ArbitraryIO implementation does not implement equals. --- .../src/main/scala/fj/data/ArbitraryIO.scala | 22 +++ .../src/test/scala/fj/CheckSemigroup.scala | 152 ++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 props-core-scalacheck/src/main/scala/fj/data/ArbitraryIO.scala create mode 100644 props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala diff --git a/props-core-scalacheck/src/main/scala/fj/data/ArbitraryIO.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryIO.scala new file mode 100644 index 00000000..7667840b --- /dev/null +++ b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryIO.scala @@ -0,0 +1,22 @@ +package fj.data + +import org.scalacheck.Arbitrary + +/** + * + */ +object ArbitraryIO { + + implicit def arbitraryIO[T](implicit arbT: Arbitrary[T]): Arbitrary[IO[T]] = + Arbitrary(arbT.arbitrary.map(t => new IO[T]() { + + override def run(): T = t + + override def toString: String = t.toString + + override def hashCode(): Int = t.hashCode() + + override def equals(obj: scala.Any): Boolean = ??? + })) + +} diff --git a/props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala b/props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala new file mode 100644 index 00000000..933bedc6 --- /dev/null +++ b/props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala @@ -0,0 +1,152 @@ +package fj + +import fj.ArbitraryP._ +import fj.ArbitraryUnit.arbitraryUnit +import fj.Ord.intOrd +import fj.data.ArbitraryArray.arbitraryArray +import fj.data.ArbitraryIO.arbitraryIO +import fj.data.ArbitraryList.arbitraryList +import fj.data.ArbitraryNatural.arbitraryNatural +import fj.data.ArbitraryNonEmptyList.arbitraryNonEmptyList +import fj.data.ArbitraryOption.arbitraryOption +import fj.data.ArbitrarySet.arbitrarySet +import fj.data.ArbitraryStream.arbitraryStream +import org.scalacheck.Arbitrary._ +import org.scalacheck.Prop._ +import org.scalacheck.{Arbitrary, Properties} + +/** + * Scalacheck [[Properties]] for [[Semigroup]] implementations. + * + * @param s a Semigroup implementation. + * @param desc a description of the Semigroup implementation. + * @param arbitrary a Scalacheck [[Arbitrary]] implementation for the Semigroup's type. + * @tparam T the type to which the Semigroup applies. + */ +case class SemigroupProperties[T](s: Semigroup[T], desc: String)(implicit val arbitrary: Arbitrary[T]) extends Properties(desc) { + + + property("sum(x,y)") = forAll((x: T, y: T, z: T) => + s.sum(s.sum(x, y), z) == s.sum(x, s.sum(y, z))) + + property("sum()") = forAll((x: T, y: T, z: T) => { + val sf = s.sum() + sf.f(sf.f(x).f(y)).f(z) == sf.f(x).f(sf.f(y).f(z)) + }) + + property("dual()") = forAll((x: T, y: T, z: T) => { + val sd = s.dual() + sd.sum(sd.sum(x, y), z) == sd.sum(x, sd.sum(y, z)) + }) + + property("sum(x)") = forAll((x: T, y: T) => + s.sum(x).f(y) == s.sum(x, y)) + +} + + +/** + * + */ +object CheckSemigroup extends Properties("Semigroup") { + + + def idInt(n: Int): java.lang.Integer = n + + implicit def oi: Ord[Int] = intOrd.contramap(idInt _) + + implicit lazy val arbJavaBigDecimal: Arbitrary[java.math.BigDecimal] = Arbitrary( + Arbitrary.arbLong.arbitrary.map(l => new java.math.BigDecimal(l)) + ) + + implicit lazy val arbJavaBigInteger: Arbitrary[java.math.BigInteger] = Arbitrary( + Arbitrary.arbBigInt.arbitrary.map(_.bigInteger) + ) + + implicit lazy val arbJavaInteger: Arbitrary[Integer] = Arbitrary( + Arbitrary.arbInt.arbitrary.map(i => i) + ) + + implicit lazy val arbJavaLong: Arbitrary[java.lang.Long] = Arbitrary( + Arbitrary.arbLong.arbitrary.map(i => i) + ) + + implicit lazy val arbJavaBoolean: Arbitrary[java.lang.Boolean] = Arbitrary( + Arbitrary.arbBool.arbitrary.map(b => b) + ) + + implicit lazy val arbStringBuilder: Arbitrary[java.lang.StringBuilder] = Arbitrary( + Arbitrary.arbString.arbitrary.map(new java.lang.StringBuilder(_)) + ) + + implicit lazy val arbStringBuffer: Arbitrary[StringBuffer] = Arbitrary( + Arbitrary.arbString.arbitrary.map(new StringBuffer(_)) + ) + + include(SemigroupProperties(Semigroup.arraySemigroup[Int](), "arraySemigroup()")) + + include(SemigroupProperties(Semigroup.bigdecimalAdditionSemigroup, "bigdecimalAdditionSemigroup")) + include(SemigroupProperties(Semigroup.bigdecimalMultiplicationSemigroup, "bigdecimalMultiplicationSemigroup")) + include(SemigroupProperties(Semigroup.bigDecimalMaximumSemigroup, "bigDecimalMaximumSemigroup")) + include(SemigroupProperties(Semigroup.bigDecimalMinimumSemigroup, "bigDecimalMinimumSemigroup")) + + include(SemigroupProperties(Semigroup.bigintAdditionSemigroup, "bigintAdditionSemigroup")) + include(SemigroupProperties(Semigroup.bigintMultiplicationSemigroup, "bigintMultiplicationSemigroup")) + include(SemigroupProperties(Semigroup.bigintMaximumSemigroup, "bigintMaximumSemigroup")) + include(SemigroupProperties(Semigroup.bigintMinimumSemigroup, "bigintMinimumSemigroup")) + + include(SemigroupProperties(Semigroup.conjunctionSemigroup, "conjunctionSemigroup")) + include(SemigroupProperties(Semigroup.disjunctionSemigroup, "disjunctionSemigroup")) + include(SemigroupProperties(Semigroup.exclusiveDisjunctionSemiGroup, "exclusiveDisjunctionSemiGroup")) + + include(SemigroupProperties(Semigroup.firstOptionSemigroup[Int](), "firstOptionSemigroup()")) + include(SemigroupProperties(Semigroup.firstSemigroup[Int](), "firstSemigroup()")) + + include(SemigroupProperties(Semigroup.intAdditionSemigroup, "intAdditionSemigroup")) + include(SemigroupProperties(Semigroup.intMultiplicationSemigroup, "intMultiplicationSemigroup")) + include(SemigroupProperties(Semigroup.intMaximumSemigroup, "intMaximumSemigroup")) + include(SemigroupProperties(Semigroup.intMinimumSemigroup, "intMinimumSemigroup")) + + include(SemigroupProperties(Semigroup.ioSemigroup[String](Semigroup.stringSemigroup), "ioSemigroup(Semigroup)")) + + include(SemigroupProperties(Semigroup.lastOptionSemigroup[Int](), "lastOptionSemigroup()")) + include(SemigroupProperties(Semigroup.lastSemigroup[Int](), "lastSemigroup()")) + + include(SemigroupProperties(Semigroup.longAdditionSemigroup, "longAdditionSemigroup")) + include(SemigroupProperties(Semigroup.longMultiplicationSemigroup, "longMultiplicationSemigroup")) + include(SemigroupProperties(Semigroup.longMaximumSemigroup, "longMaximumSemigroup")) + include(SemigroupProperties(Semigroup.longMinimumSemigroup, "longMinimumSemigroup")) + + + include(SemigroupProperties(Semigroup.listSemigroup[Int], "listSemigroup")) + + include(SemigroupProperties(Semigroup.naturalAdditionSemigroup, "naturalAdditionSemigroup")) + include(SemigroupProperties(Semigroup.naturalMaximumSemigroup, "naturalMaximumSemigroup")) + include(SemigroupProperties(Semigroup.naturalMinimumSemigroup, "naturalMinimumSemigroup")) + include(SemigroupProperties(Semigroup.naturalMultiplicationSemigroup, "naturalMultiplicationSemigroup")) + + include(SemigroupProperties(Semigroup.nonEmptyListSemigroup[Int], "nonEmptyListSemigroup")) + + include(SemigroupProperties(Semigroup.p1Semigroup(Semigroup.intAdditionSemigroup), "p1Semigroup(Semigroup)")) + include(SemigroupProperties(Semigroup.p2Semigroup(Semigroup.intAdditionSemigroup, Semigroup.stringSemigroup), "p2Semigroup(Semigroup,Semigroup)")) + + include(SemigroupProperties(Semigroup.semigroup[Int](new F2[Int, Int, Int] { + def f(x: Int, y: Int): Int = x + y + }), "semigroup(F)")) + + include(SemigroupProperties(Semigroup.semigroup[Int](new F[Int, F[Int, Int]] { + def f(x: Int): F[Int, Int] = (y: Int) => x + y + }), "semigroup(F>)")) + + include(SemigroupProperties(Semigroup.setSemigroup[Int](), "setSemigroup()")) + include(SemigroupProperties(Semigroup.streamSemigroup[Int](), "streamSemigroup")) + + include(SemigroupProperties(Semigroup.stringSemigroup, "stringSemigroup")) + include(SemigroupProperties(Semigroup.stringBufferSemigroup, "stringBufferSemigroup")) + include(SemigroupProperties(Semigroup.stringBuilderSemigroup, "stringBuilderSemigroup")) + + + include(SemigroupProperties(Semigroup.unitSemigroup, "unitSemigroup")) + + +} From a8e979fc78e0cdd4e33e86d32b0394edcc065e47 Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Tue, 2 May 2017 22:51:29 -0500 Subject: [PATCH 014/173] Fixed the ArbitraryIO implementation and created a Properties object for testing the IO semigroup. --- .../src/main/scala/fj/data/ArbitraryIO.scala | 14 ++--- .../src/test/scala/fj/CheckSemigroup.scala | 57 ++++++++++++++++++- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/props-core-scalacheck/src/main/scala/fj/data/ArbitraryIO.scala b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryIO.scala index 7667840b..aca5cbd1 100644 --- a/props-core-scalacheck/src/main/scala/fj/data/ArbitraryIO.scala +++ b/props-core-scalacheck/src/main/scala/fj/data/ArbitraryIO.scala @@ -7,16 +7,12 @@ import org.scalacheck.Arbitrary */ object ArbitraryIO { - implicit def arbitraryIO[T](implicit arbT: Arbitrary[T]): Arbitrary[IO[T]] = - Arbitrary(arbT.arbitrary.map(t => new IO[T]() { - - override def run(): T = t - override def toString: String = t.toString + private case class ArbIO[T](value: T) extends IO[T] { + override def run(): T = value + } - override def hashCode(): Int = t.hashCode() - - override def equals(obj: scala.Any): Boolean = ??? - })) + implicit def arbitraryIO[T](implicit arbT: Arbitrary[T]): Arbitrary[IO[T]] = + Arbitrary(arbT.arbitrary.map(ArbIO(_))) } diff --git a/props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala b/props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala index 933bedc6..1305efe9 100644 --- a/props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala +++ b/props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala @@ -11,12 +11,13 @@ import fj.data.ArbitraryNonEmptyList.arbitraryNonEmptyList import fj.data.ArbitraryOption.arbitraryOption import fj.data.ArbitrarySet.arbitrarySet import fj.data.ArbitraryStream.arbitraryStream +import fj.data.IO import org.scalacheck.Arbitrary._ import org.scalacheck.Prop._ import org.scalacheck.{Arbitrary, Properties} /** - * Scalacheck [[Properties]] for [[Semigroup]] implementations. + * Scalacheck [[Properties]] parameterized for [[Semigroup]] implementations. * * @param s a Semigroup implementation. * @param desc a description of the Semigroup implementation. @@ -44,9 +45,59 @@ case class SemigroupProperties[T](s: Semigroup[T], desc: String)(implicit val ar } +/** + * A specialized Scalacheck [[Properties]] object for testing the [[Semigroup.ioSemigroup()]] method. + */ +object CheckIOSemigroup extends Properties("ioSemigroup") { + + val s = Semigroup.ioSemigroup(Semigroup.stringSemigroup) + + property("sum(x,y)") = forAll((x: IO[String], y: IO[String], z: IO[String]) => + s.sum(s.sum(x, y), z).run() == s.sum(x, s.sum(y, z)).run()) + + property("sum()") = forAll((x: IO[String], y: IO[String], z: IO[String]) => { + val sf = s.sum() + sf.f(sf.f(x).f(y)).f(z).run() == sf.f(x).f(sf.f(y).f(z)).run() + }) + + property("dual()") = forAll((x: IO[String], y: IO[String], z: IO[String]) => { + val sd = s.dual() + sd.sum(sd.sum(x, y), z).run() == sd.sum(x, sd.sum(y, z)).run() + }) + + property("sum(x)") = forAll((x: IO[String], y: IO[String]) => + s.sum(x).f(y).run() == s.sum(x, y).run()) + +} + +object CheckStringBuilder extends Properties("stringBuilderSemigroup") { + + val s: Semigroup[java.lang.StringBuilder] = Semigroup.stringBuilderSemigroup + + implicit def toStringBuilder(str: String): java.lang.StringBuilder = new java.lang.StringBuilder(str) + + property("sum(x,y)") = forAll((x: String, y: String, z: String) => + s.sum(s.sum(x, y), z).toString == s.sum(x, s.sum(y, z)).toString + ) + + property("sum()") = forAll((x: String, y: String, z: String) => { + val sf = s.sum() + sf.f(sf.f(x).f(y)).f(z).toString == sf.f(x).f(sf.f(y).f(z.toString)) + }) + + property("dual()") = forAll((x: String, y: String, z: String) => { + val sd = s.dual() + sd.sum(sd.sum(x, y), z).toString == sd.sum(x, sd.sum(y, z).toString) + }) + + property("sum(x)") = forAll((x: String, y: String) => + s.sum(x).f(y).toString == s.sum(x, y).toString) + +} + /** - * + * A Scalacheck [[Properties]] object that aggregates the tests for all [[Semigroup]] implementations. */ object CheckSemigroup extends Properties("Semigroup") { @@ -107,7 +158,7 @@ object CheckSemigroup extends Properties("Semigroup") { include(SemigroupProperties(Semigroup.intMaximumSemigroup, "intMaximumSemigroup")) include(SemigroupProperties(Semigroup.intMinimumSemigroup, "intMinimumSemigroup")) - include(SemigroupProperties(Semigroup.ioSemigroup[String](Semigroup.stringSemigroup), "ioSemigroup(Semigroup)")) + include(CheckIOSemigroup) include(SemigroupProperties(Semigroup.lastOptionSemigroup[Int](), "lastOptionSemigroup()")) include(SemigroupProperties(Semigroup.lastSemigroup[Int](), "lastSemigroup()")) From aa4de33faacb59c7dd24092fe3d3a65ca3a4c4d9 Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Tue, 9 May 2017 22:47:43 -0500 Subject: [PATCH 015/173] Added working tests coverage for the StringBuffer and StringBuilder semigroup implementations. Added the Semigroup tests to the list of Scalacheck test suite. --- .../src/test/scala/fj/CheckSemigroup.scala | 33 ++++++++++++------- .../src/test/scala/fj/Tests.scala | 30 +++++++++-------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala b/props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala index 1305efe9..8c9fe547 100644 --- a/props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala +++ b/props-core-scalacheck/src/test/scala/fj/CheckSemigroup.scala @@ -70,27 +70,36 @@ object CheckIOSemigroup extends Properties("ioSemigroup") { } -object CheckStringBuilder extends Properties("stringBuilderSemigroup") { - - val s: Semigroup[java.lang.StringBuilder] = Semigroup.stringBuilderSemigroup +/** + * A [[Properties]] implementation for testing [[Semigroup]] implementations that + * apply to mutable builder style classes such as [[java.lang.StringBuilder]] and + * [[java.lang.StringBuffer]]. + * + * @param s a Semigroup implementation to test. + * @param desc a description of the Semigroup implementation under test. + * @param conversion a function that converts a value of type T to a new instance of type S. + * @tparam T the type that the builder constructs. + * @tparam S the type to which the Semigroup applies. + */ +case class CheckMutableBuilder[S, T](s: Semigroup[S], desc: String, conversion: T => S)(implicit val arbitrary: Arbitrary[T]) extends Properties(desc) { - implicit def toStringBuilder(str: String): java.lang.StringBuilder = new java.lang.StringBuilder(str) + implicit def toBuilder(t: T): S = conversion(t) - property("sum(x,y)") = forAll((x: String, y: String, z: String) => + property("sum(x,y)") = forAll((x: T, y: T, z: T) => s.sum(s.sum(x, y), z).toString == s.sum(x, s.sum(y, z)).toString ) - property("sum()") = forAll((x: String, y: String, z: String) => { + property("sum()") = forAll((x: T, y: T, z: T) => { val sf = s.sum() - sf.f(sf.f(x).f(y)).f(z).toString == sf.f(x).f(sf.f(y).f(z.toString)) + sf.f(sf.f(x).f(y)).f(z).toString == sf.f(x).f(sf.f(y).f(z)).toString }) - property("dual()") = forAll((x: String, y: String, z: String) => { + property("dual()") = forAll((x: T, y: T, z: T) => { val sd = s.dual() - sd.sum(sd.sum(x, y), z).toString == sd.sum(x, sd.sum(y, z).toString) + sd.sum(sd.sum(x, y), z).toString == sd.sum(x, sd.sum(y, z)).toString }) - property("sum(x)") = forAll((x: String, y: String) => + property("sum(x)") = forAll((x: T, y: T) => s.sum(x).f(y).toString == s.sum(x, y).toString) } @@ -193,8 +202,8 @@ object CheckSemigroup extends Properties("Semigroup") { include(SemigroupProperties(Semigroup.streamSemigroup[Int](), "streamSemigroup")) include(SemigroupProperties(Semigroup.stringSemigroup, "stringSemigroup")) - include(SemigroupProperties(Semigroup.stringBufferSemigroup, "stringBufferSemigroup")) - include(SemigroupProperties(Semigroup.stringBuilderSemigroup, "stringBuilderSemigroup")) + include(CheckMutableBuilder(Semigroup.stringBufferSemigroup, "stringBufferSemigroup", (s: String) => new java.lang.StringBuffer(s))) + include(CheckMutableBuilder(Semigroup.stringBuilderSemigroup, "stringBuilderSemigroup", (s: String) => new java.lang.StringBuilder(s))) include(SemigroupProperties(Semigroup.unitSemigroup, "unitSemigroup")) diff --git a/props-core-scalacheck/src/test/scala/fj/Tests.scala b/props-core-scalacheck/src/test/scala/fj/Tests.scala index 60c2f009..d94661f9 100644 --- a/props-core-scalacheck/src/test/scala/fj/Tests.scala +++ b/props-core-scalacheck/src/test/scala/fj/Tests.scala @@ -1,8 +1,9 @@ package fj object Tests { - def tests = List ( + def tests = List( CheckP2.properties, + CheckSemigroup.properties, fj.data.CheckArray.properties, fj.data.CheckIO.properties, fj.data.CheckIteratee.properties, @@ -19,26 +20,27 @@ object Tests { def main(args: Array[String]) { run(tests) -// System.exit(0) + // System.exit(0) } - import org.scalacheck.Prop - import org.scalacheck.Test import org.scalacheck.Test.check + import org.scalacheck.{Prop, Test} def run(tests: List[(String, Prop)]) = tests foreach { case (name, p) => { - val c = check(new Test.Parameters.Default { override val maxSize = 20 }, p) - c.status match { - case Test.Passed => println("Passed " + name) - case Test.Proved(_) => println("Proved " + name) - case f @ Test.Failed(_, _) => sys.error(name + ": " + f) - case Test.Exhausted => println("Exhausted " + name) - case f @ Test.PropException(_, e, _) => { - e.printStackTrace - sys.error(name + ": " + f) - } + val c = check(new Test.Parameters.Default { + override val maxSize = 20 + }, p) + c.status match { + case Test.Passed => println("Passed " + name) + case Test.Proved(_) => println("Proved " + name) + case f@Test.Failed(_, _) => sys.error(name + ": " + f) + case Test.Exhausted => println("Exhausted " + name) + case f@Test.PropException(_, e, _) => { + e.printStackTrace + sys.error(name + ": " + f) } } } + } } From e834e8bfb87c394b0071e478a84445a1ca88834e Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Wed, 9 Aug 2017 13:34:53 +0200 Subject: [PATCH 016/173] Enable upload of snapshot artifacts. --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8f3091d1..4a9f2d24 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,3 +30,10 @@ script: after_success: - bash <(curl -s https://codecov.io/bash) + - '[ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" -a -z "$TRAVIS_TAG" ] + && ./gradlew uploadArchives' + +env: + global: + - secure: Bun+1FZ29Q3dR9gZ/5brxcSf+zcY5tWrsqOA4GUb5bYCMyORuXQB0FYXuhKR4wB1pFrk1a9EYwRwSu3GwRJVWb+UzF0CNOWF/QG5tGPx32IOXScwlL/KonI4Vhs7Oc0fF4Wdb7euNrT27BU61jbUugjJ642b3n0VBYFYDdquprU= + - secure: QAxhjqLRa+WHKIzgIJPZ/rM5a5uzqG7E5rsC0YvB25cO712oYXmzsYPia/oSp0chXlYLYMfk2UnLeQCSx2e6ogXRRRa977Q+B33Nt0Hd9SGLtduv6DBrbA2ehLU12Ib4DWe5VhF5eueAunycYcllTvqA5h+pzTtEVbd68ZHncM4= From 8330696120a4bcb744b1601600cb573dc176d0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sat, 9 Sep 2017 16:29:09 -0400 Subject: [PATCH 017/173] Upgrade Gradle version to 4.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- gradle/wrapper/gradle-wrapper.jar | Bin 54208 -> 54708 bytes gradle/wrapper/gradle-wrapper.properties | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 2dcd00d1ccd90b5b3d39e0da139893d5e7651ae4..7a3265ee94c0ab25cf079ac8ccdf87f41d455d42 100644 GIT binary patch delta 19176 zcmZ6yV{m3&w>2Ew?6_mwwr$(Cadpzk6(=3rwylnB+qRv&_j&H~)mP8?v1`rRHP^0M zRb!8-vHq5TH&%cnDanFEV1R(Yz<_{&2!q5Sk^f@|gK)(i$;9z$V5gtp+_~JkfP(xV zx6+>w|8bw7{C9n3{Lk9pK+OU9A1k4hVdpL(FvcSWu!X=@u3r5y z{&N>8=QoGFZncMeBZ+ejqAmiv=%4C)ob7te?`iRLT;nJRl6bo!fyRx8{r=D9z5UL4ODV`5f(yi zVZh^C*&pM4cV;SH4@jv65Rk`0EoM7JD!n6C7nwL2gybnT{qpPx;woxaqpvCQVns19 zyj75-x(>Mr4Lq2fY=ERe7^lzkr0QZ+cLlpQAE}LFT-d%x-hdBzvPhu8x?=fd%rDsD z*=7P2i=Xtv|Q=c`3^TFdX&KOq&?uixhEl zg8vOx(gsDXO~u<93tikV3LpC;;@dM7Ax*Hn_}5|^I%{EQI4H92)>UrBAhGD&Mr8cJ z0ywbB&p#+XiKv+-q=6Vn$r|>Znw}jK2SaabUtKQajD|Xn{bY--Xa$EV7Y@6iiQ#7*7;x)!8K}A8I^6 z@*xY4w>JpSvkIN8d#{iAVp{c3Kt6FiW(+m8ugSTs#EjIG-mW=$B8+Ltq!Uw*sJII0 zmD4G$Qq}+sQZFdF!YoeV1M0s~^XC?I3Jw_w1cU+~1cVqQL3sc(fe#M`z+h@;?CP4O z4R3_8?9Q*awuuuhL8Kv7Yjk7s7wZid?>9Yy1R5AbJc@_!j^nf4`b}QYdhUKJpi##jqon!q z$p6a!x!ZTT>$>~#xaaj0Fb%ds&G18Nr#;ZPQ>}l?((GP)wUaJG^Wco}U;fEJykI9bat@r9jdf}`4>IX1z{WcxAy>O!om434h)%QXG-V?c`Y` z*Ei2iFVLM1J4CnY1gtJcIR^@c39N?mH`6LFX`|J8w@$f-)nn+w2HHe-Kwbn0>Y|vZ>u_Ni33K3Fim)xjX9YAXMA6gbj(q+#rR%-O}J> zd)S#tB!k23phjCShrHeLKvjCHJP6oP?*57aU`(duvQfxu!IhEz!Qw-E{Wd{0`Pv8U zvA}c~ITmri1o?>whlrcvClut_n?B{GZj7LD5sODtV}TIBe)}a)6x@u{iLJhP zRZ@Co<|;FQ80EwheWBBs>U4!PtxbBJ*IK@KL3@LmmfhG}ntMtS>SL8mzRQ|DsVu*v z++usudHaOfA>AeG^gwQ?VO%+P!eEmo{6Wkh>hEM#n?L^gH6an+#C5t`2Qs;8^O|x4 z8f+OHb7N+rlJttT;OW70wc(U(D6(F9{qZ458Xi1=1)5;{37=NnoW-ErZkf1#(Q>YN znb-8#SXxriqPT=yqJ)S7~^ zqGthEB{*E#zfDNDDk9TqotB|28S82)$amE!r`t4>!yI<2W!ueVy=38*)3Nq_t_HP~ z*?nJHf@>|qsETH7&N00M+5?t~4mlEMyr0}6=Gt9t3pwq@^>P
    ;{uxCFdo17>QTVNzIrR=a?@u;vR$fngPL2ck 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
      9(Ta0+uZQM3v=vl(NR&l<6Sp%Q$O6-L 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7a57ac51..f16d2666 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Mar 27 13:51:02 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip From 95c0d0d659a2dfa354aa549aa6197be7d12aba15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sat, 9 Sep 2017 17:38:22 -0400 Subject: [PATCH 018/173] Bring dependencies current MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- build.gradle | 2 +- core/build.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 04209664..6f8ecc5d 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ ext { buildscript { ext { - uptodateVersion = "1.6.2" + uptodateVersion = "1.6.3" retrolambdaPluginVersion = "3.5.0" retrolambdaVersion = "2.5.1" } diff --git a/core/build.gradle b/core/build.gradle index b3e1756e..e0c86f0c 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -7,8 +7,8 @@ archivesBaseName = project.projectName dependencies { testCompile dependencyJunit - testCompile 'com.h2database:h2:1.4.191' - testCompile 'commons-dbutils:commons-dbutils:1.6' + testCompile 'com.h2database:h2:1.4.196' + testCompile 'commons-dbutils:commons-dbutils:1.7' } performSigning(signingEnabled, signModule) From 6c6dabdb26b050c5d0e3a712fe4f28edd0e1ad57 Mon Sep 17 00:00:00 2001 From: l1cache Date: Mon, 11 Sep 2017 14:19:59 +0300 Subject: [PATCH 019/173] Equal: remove reference to static field of LazyString. Fix #321 Cause it could cause deadlock on parallel static initialization. --- core/src/main/java/fj/Equal.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/fj/Equal.java b/core/src/main/java/fj/Equal.java index b64c758d..5e407a00 100644 --- a/core/src/main/java/fj/Equal.java +++ b/core/src/main/java/fj/Equal.java @@ -674,7 +674,7 @@ public static Equal> v8Equal(final Equal ea) { /** * An equal instance for lazy strings. */ - public static final Equal eq = streamEqual(charEqual).contramap(LazyString.toStream); + public static final Equal eq = streamEqual(charEqual).contramap(LazyString::toStream); /** * An equal instance for the empty heterogeneous list. From 065ed439303eacf90e3ac49c5403b1d62979b8f4 Mon Sep 17 00:00:00 2001 From: janbols Date: Mon, 6 Nov 2017 20:09:10 +0100 Subject: [PATCH 020/173] Add append methods to all Px classes. Fix #326 --- core/src/main/java/fj/P2.java | 63 ++++++++++++++++++++++++++++++++++- core/src/main/java/fj/P3.java | 52 +++++++++++++++++++++++++++++ core/src/main/java/fj/P4.java | 44 ++++++++++++++++++++++++ core/src/main/java/fj/P5.java | 30 +++++++++++++++++ core/src/main/java/fj/P6.java | 20 +++++++++++ core/src/main/java/fj/P7.java | 10 ++++++ 6 files changed, 218 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/fj/P2.java b/core/src/main/java/fj/P2.java index e503ff40..b434a0da 100644 --- a/core/src/main/java/fj/P2.java +++ b/core/src/main/java/fj/P2.java @@ -178,7 +178,68 @@ public final P1 _2_() { return F1Functions.lazy(P2.__2()).f(this); } - /** + /** + * Creates a {@link P3} by adding the given element to the current {@link P2} + * + * @param el the element to append + * @return A {@link P3} containing the original {@link P2} with the extra element added at the end + */ + public final P3 append(C el) { + return P.p(_1(), _2(), el); + } + + /** + * Creates a {@link P4} by adding the given element to the current {@link P2} + * + * @param el the element to append + * @return A {@link P4} containing the original {@link P2} with the extra element added at the end + */ + public final P4 append(P2 el) { + return P.p(_1(), _2(), el._1(), el._2()); + } + + /** + * Creates a {@link P5} by adding the given element to the current {@link P2} + * + * @param el the element to append + * @return A {@link P5} containing the original {@link P2} with the extra element added at the end + */ + public final P5 append(P3 el) { + return P.p(_1(), _2(), el._1(), el._2(), el._3()); + } + + /** + * Creates a {@link P6} by adding the given element to the current {@link P2} + * + * @param el the element to append + * @return A {@link P6} containing the original {@link P2} with the extra element added at the end + */ + public final P6 append(P4 el) { + return P.p(_1(), _2(), el._1(), el._2(), el._3(), el._4()); + } + + /** + * Creates a {@link P7} by adding the given element to the current {@link P2} + * + * @param el the element to append + * @return A {@link P7} containing the original {@link P2} with the extra element added at the end + */ + public final P7 append(P5 el) { + return P.p(_1(), _2(), el._1(), el._2(), el._3(), el._4(), el._5()); + } + + /** + * Creates a {@link P8} by adding the given element to the current {@link P2} + * + * @param el the element to append + * @return A {@link P8} containing the original {@link P2} with the extra element added at the end + */ + public final P8 append(P6 el) { + return P.p(_1(), _2(), el._1(), el._2(), el._3(), el._4(), el._5(), el._6()); + } + + + /** * Provides a memoising P2 that remembers its values. * * @return A P2 that calls this P2 once for any given element and remembers the value for subsequent calls. diff --git a/core/src/main/java/fj/P3.java b/core/src/main/java/fj/P3.java index ec277346..fe980059 100644 --- a/core/src/main/java/fj/P3.java +++ b/core/src/main/java/fj/P3.java @@ -122,6 +122,58 @@ public final P1 _3_() { return F1Functions.lazy(P3.__3()).f(this); } + + /** + * Creates a {@link P4} by adding the given element to the current {@link P3} + * + * @param el the element to append + * @return A {@link P4} containing the original {@link P3} with the extra element added at the end + */ + public final P4 append(D el) { + return P.p(_1(), _2(), _3(), el); + } + + /** + * Creates a {@link P5} by adding the given element to the current {@link P3} + * + * @param el the element to append + * @return A {@link P5} containing the original {@link P3} with the extra element added at the end + */ + public final P5 append(P2 el) { + return P.p(_1(), _2(), _3(), el._1(), el._2()); + } + + /** + * Creates a {@link P6} by adding the given element to the current {@link P3} + * + * @param el the element to append + * @return A {@link P6} containing the original {@link P3} with the extra element added at the end + */ + public final P6 append(P3 el) { + return P.p(_1(), _2(), _3(), el._1(), el._2(), el._3()); + } + + /** + * Creates a {@link P7} by adding the given element to the current {@link P3} + * + * @param el the element to append + * @return A {@link P7} containing the original {@link P3} with the extra element added at the end + */ + public final P7 append(P4 el) { + return P.p(_1(), _2(), _3(), el._1(), el._2(), el._3(), el._4()); + } + + /** + * Creates a {@link P8} by adding the given element to the current {@link P3} + * + * @param el the element to append + * @return A {@link P8} containing the original {@link P3} with the extra element added at the end + */ + public final P8 append(P5 el) { + return P.p(_1(), _2(), _3(), el._1(), el._2(), el._3(), el._4(), el._5()); + } + + /** * Provides a memoising P3 that remembers its values. * diff --git a/core/src/main/java/fj/P4.java b/core/src/main/java/fj/P4.java index 3f7236fe..a7bf8167 100644 --- a/core/src/main/java/fj/P4.java +++ b/core/src/main/java/fj/P4.java @@ -176,6 +176,50 @@ public final P1 _4_() { return F1Functions.lazy(P4.__4()).f(this); } + + /** + * Creates a {@link P5} by adding the given element to the current {@link P4} + * + * @param el the element to append + * @return A {@link P5} containing the original {@link P4} with the extra element added at the end + */ + public final P5 append(E el) { + return P.p(_1(), _2(), _3(), _4(), el); + } + + /** + * Creates a {@link P6} by adding the given element to the current {@link P4} + * + * @param el the element to append + * @return A {@link P6} containing the original {@link P4} with the extra element added at the end + */ + public final P6 append(P2 el) { + return P.p(_1(), _2(), _3(), _4(), el._1(), el._2()); + } + + /** + * Creates a {@link P7} by adding the given element to the current {@link P4} + * + * @param el the element to append + * @return A {@link P7} containing the original {@link P4} with the extra element added at the end + */ + public final P7 append(P3 el) { + return P.p(_1(), _2(), _3(), _4(), el._1(), el._2(), el._3()); + } + + /** + * Creates a {@link P8} by adding the given element to the current {@link P4} + * + * @param el the element to append + * @return A {@link P8} containing the original {@link P4} with the extra element added at the end + */ + public final P8 append(P4 el) { + return P.p(_1(), _2(), _3(), _4(), el._1(), el._2(), el._3(), el._4()); + } + + + + /** * Provides a memoising P4 that remembers its values. * diff --git a/core/src/main/java/fj/P5.java b/core/src/main/java/fj/P5.java index 393b0632..69262327 100644 --- a/core/src/main/java/fj/P5.java +++ b/core/src/main/java/fj/P5.java @@ -238,6 +238,36 @@ public final P1 _5_() { return F1Functions.lazy(P5.__5()).f(this); } + /** + * Creates a {@link P6} by adding the given element to the current {@link P5} + * + * @param el the element to append + * @return A {@link P6} containing the original {@link P5} with the extra element added at the end + */ + public final P6 append(F el) { + return P.p(_1(), _2(), _3(), _4(), _5(), el); + } + + /** + * Creates a {@link P7} by adding the given element to the current {@link P5} + * + * @param el the element to append + * @return A {@link P7} containing the original {@link P5} with the extra element added at the end + */ + public final P7 append(P2 el) { + return P.p(_1(), _2(), _3(), _4(), _5(), el._1(), el._2()); + } + + /** + * Creates a {@link P8} by adding the given element to the current {@link P5} + * + * @param el the element to append + * @return A {@link P8} containing the original {@link P5} with the extra element added at the end + */ + public final P8 append(P3 el) { + return P.p(_1(), _2(), _3(), _4(), _5(), el._1(), el._2(), el._3()); + } + /** * Provides a memoising P5 that remembers its values. * diff --git a/core/src/main/java/fj/P6.java b/core/src/main/java/fj/P6.java index 1e8c3055..fbf91948 100644 --- a/core/src/main/java/fj/P6.java +++ b/core/src/main/java/fj/P6.java @@ -309,6 +309,26 @@ public final P1 _6_() { return F1Functions.lazy(P6.__6()).f(this); } + /** + * Creates a {@link P7} by adding the given element to the current {@link P6} + * + * @param el the element to append + * @return A {@link P7} containing the original {@link P6} with the extra element added at the end + */ + public final P7 append(G el) { + return P.p(_1(), _2(), _3(), _4(), _5(), _6(), el); + } + + /** + * Creates a {@link P8} by adding the given element to the current {@link P6} + * + * @param el the element to append + * @return A {@link P8} containing the original {@link P6} with the extra element added at the end + */ + public final P8 append(P2 el) { + return P.p(_1(), _2(), _3(), _4(), _5(), _6(), el._1(), el._2()); + } + /** * Provides a memoising P6 that remembers its values. * diff --git a/core/src/main/java/fj/P7.java b/core/src/main/java/fj/P7.java index ca24b40e..b1c67b30 100644 --- a/core/src/main/java/fj/P7.java +++ b/core/src/main/java/fj/P7.java @@ -387,6 +387,16 @@ public final P1 _7_() { return F1Functions.lazy(P7.__7()).f(this); } + /** + * Creates a {@link P8} by adding the given element to the current {@link P7} + * + * @param el the element to append + * @return A {@link P8} containing the original {@link P7} with the extra element added at the end + */ + public final P8 append(H el) { + return P.p(_1(), _2(), _3(), _4(), _5(), _6(), _7(), el); + } + /** * Provides a memoising P7 that remembers its values. * From 34f2c176ac462596f22de81a70fd0bfdda8f6ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sat, 9 Sep 2017 17:44:31 -0400 Subject: [PATCH 021/173] Bring ScalaCheck current MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- props-core-scalacheck/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/props-core-scalacheck/build.gradle b/props-core-scalacheck/build.gradle index 02f3652a..a395a586 100644 --- a/props-core-scalacheck/build.gradle +++ b/props-core-scalacheck/build.gradle @@ -4,9 +4,9 @@ archivesBaseName = "${project.projectName}-${project.name}" apply plugin: 'scala' ext { - scalaVersion = "2.11.8" + scalaVersion = "2.11.11" scalacheckScalaVersion = "2.11" - scalacheckVersion = "1.12.5" + scalacheckVersion = "1.12.6" signModule = true } From 98294fc3bdffe6c290818a6ffc04227e44ea3719 Mon Sep 17 00:00:00 2001 From: Iaroslav Zeigerman Date: Wed, 17 Jan 2018 08:51:51 -0500 Subject: [PATCH 022/173] Introduce the Eval monad a convenient wrapper around Trampoline and P1.hardMemo for stack-safe computation. --- core/src/main/java/fj/data/Eval.java | 238 +++++++++++++++++++++++ core/src/test/java/fj/data/EvalTest.java | 76 ++++++++ 2 files changed, 314 insertions(+) create mode 100644 core/src/main/java/fj/data/Eval.java create mode 100644 core/src/test/java/fj/data/EvalTest.java diff --git a/core/src/main/java/fj/data/Eval.java b/core/src/main/java/fj/data/Eval.java new file mode 100644 index 00000000..519070a1 --- /dev/null +++ b/core/src/main/java/fj/data/Eval.java @@ -0,0 +1,238 @@ +package fj.data; + +import fj.F; +import fj.F0; +import fj.P; +import fj.P1; +import fj.control.Trampoline; + +/** + * Eval is an abstraction over different models of evaluation. + * The data constructors: + *
        + *
      • Now - the value is evaluated immediately.
      • + *
      • Later - the value is evaluated only once when it's requested (lazy evaluation).
      • + *
      • Always - the value is evaluated every time when it's requested.
      • + *
      + * + * Both Later and Always are lazy computations, while Now is eager. + * + * + * @version %build.number% + */ +public abstract class Eval
      { + + /** + * Constructs an eager evaluation by wrapping the given value. + * + * @param a the evaluated value. + * @return an eval with computed value. + */ + public static Eval now(A a) { + return new Now<>(a); + } + + /** + * Constructs a lazy evaluation with caching. + * + * @param a the supplier that evaluates a value. + * @return a lazy evaluation. + */ + public static Eval later(F0 a) { + return new Later<>(a); + } + + /** + * Constructs a lazy evaluation without caching. + * + * @param a the supplier that evaluates a value. + * @return a lazy evaluation. + */ + public static Eval always(F0 a) { + return new Always<>(a); + } + + /** + * Constructs a lazy evaluation of an expression that produces Eval. + * This operation is stack-safe and can be used for recursive computations. + * + * @param a the supplier that produces an Eval. + * @return a lazily evaluated nested evaluation. + */ + public static Eval defer(F0> a) { + return new DeferEval<>(a); + } + + /** + * Evaluates the computation and return its result. + * Depending on whether the current instance is lazy or eager the + * computation may or may not happen at this point. + * + * @return a result of this computation. + */ + public abstract A value(); + + /** + * Transforms Eval into a Eval using + * the given function. + * + * Note: the computation of the given transformation is always lazy, + * even if it invoked for an eager Now instance. This computation + * is performed in O(1) stack space. + * + * @param f the transformation function. + * @return a transformed evaluation. + */ + public final Eval map(final F f) { + return bind(a -> now(f.f(a))); + } + + /** + * Alias for {@link #bind(F)}. + */ + public final Eval flatMap(final F> f) { + return bind(f); + } + + /** + * Transforms Eval into a Eval using + * the given function that directly produces Eval. + * + * Note: the computation of the given transformation is always lazy, + * even if it invoked for an eager Now instance. This computation + * is performed in O(1) stack space. + * + * @param f the transformation function. + * @return a transformed evaluation. + */ + public final Eval bind(final F> f) { + return new BindTrampolineEval<>(f, asTrampoline()); + } + + /** + * Transforms the current instance into a trampoline instance. + */ + abstract TrampolineEval asTrampoline(); + + /** + * Represents an eager computation. + */ + private static final class Now extends Eval { + private final A a; + + Now(A a) { + this.a = a; + } + + @Override + public final A value() { + return a; + } + + @Override + final TrampolineEval asTrampoline() { + return new PureTrampolineEval<>(this); + } + } + + /** + * Represents a lazy computation that is evaluated only once. + */ + private static final class Later extends Eval { + private final P1 memo; + + Later(F0 producer) { + this.memo = P.hardMemo(producer); + } + + @Override + public final A value() { + return memo._1(); + } + + @Override + final TrampolineEval asTrampoline() { + return new PureTrampolineEval<>(this); + } + } + + /** + * Represents a lazy computation that is evaluated every time when it's requested. + */ + private static final class Always extends Eval { + private final F0 supplier; + + Always(F0 supplier) { + this.supplier = supplier; + } + + @Override + public final A value() { + return supplier.f(); + } + + @Override + final TrampolineEval asTrampoline() { + return new PureTrampolineEval<>(this); + } + } + + /** + * A helper abstraction that allows to perform recursive lazy transformations in O(1) stack space. + */ + private static abstract class TrampolineEval extends Eval { + + protected abstract Trampoline trampoline(); + + @Override + public final A value() { + return trampoline().run(); + } + + @Override + final TrampolineEval asTrampoline() { + return this; + } + } + + private static final class PureTrampolineEval extends TrampolineEval { + private final Eval start; + + PureTrampolineEval(Eval start) { + this.start = start; + } + + @Override + protected final Trampoline trampoline() { + return Trampoline.suspend(P.lazy(() -> Trampoline.pure(start.value()))); + } + } + + private static final class BindTrampolineEval extends TrampolineEval { + private final TrampolineEval next; + private final F> f; + + BindTrampolineEval(F> f, TrampolineEval next) { + this.next = next; + this.f = f; + } + + @Override + protected final Trampoline trampoline() { + return Trampoline.suspend(P.lazy(() -> next.trampoline().bind(v -> f.f(v).asTrampoline().trampoline()))); + } + } + + private static final class DeferEval extends TrampolineEval { + private final P1> memo; + + DeferEval(F0> producer) { + this.memo = P.hardMemo(producer); + } + + @Override + protected final Trampoline trampoline() { + return memo._1().asTrampoline().trampoline(); + } + } +} diff --git a/core/src/test/java/fj/data/EvalTest.java b/core/src/test/java/fj/data/EvalTest.java new file mode 100644 index 00000000..a604c873 --- /dev/null +++ b/core/src/test/java/fj/data/EvalTest.java @@ -0,0 +1,76 @@ +package fj.data; + +import fj.F0; +import org.junit.Assert; +import org.junit.Test; + +public class EvalTest { + + @Test + public void testNow() { + Eval eval = Eval.now(1); + Assert.assertEquals(eval.value().intValue(), 1); + Assert.assertEquals(eval.map(a -> a.toString()).value(), "1"); + Assert.assertEquals(eval.bind(a -> Eval.now(a * 3)).value().intValue(), 3); + } + + @Test + public void testLater() { + InvocationTrackingF tracker = new InvocationTrackingF<>(1); + Eval eval = Eval.later(tracker); + + Assert.assertEquals(tracker.getInvocationCounter(), 0); + Assert.assertEquals(eval.value().intValue(), 1); + Assert.assertEquals(tracker.getInvocationCounter(), 1); + eval.value(); + Assert.assertEquals(tracker.getInvocationCounter(), 1); + } + + @Test + public void testAlways() { + InvocationTrackingF tracker = new InvocationTrackingF<>(1); + Eval eval = Eval.always(tracker); + + Assert.assertEquals(tracker.getInvocationCounter(), 0); + Assert.assertEquals(eval.value().intValue(), 1); + Assert.assertEquals(tracker.getInvocationCounter(), 1); + eval.value(); + eval.value(); + Assert.assertEquals(tracker.getInvocationCounter(), 3); + } + + @Test + public void testDefer() { + // Make sure that a recursive computation is actually stack-safe. + Assert.assertEquals(even(200000).value(), "done"); + } + + private static Eval even(int n) { + return Eval.now(n <= 0).flatMap(b -> b ? Eval.now("done") : Eval.defer(() -> odd(n - 1))); + } + + private static Eval odd(int n) { + return Eval.defer(() -> even(n - 1)); + } + + private static class InvocationTrackingF implements F0 { + + private final A value; + private int invocationCounter; + + public InvocationTrackingF(A value) { + this.value = value; + this.invocationCounter = 0; + } + + @Override + public A f() { + invocationCounter++; + return value; + } + + public int getInvocationCounter() { + return invocationCounter; + } + } +} From e0f69dc368fe0df52531dc48c490a27ae143de2c Mon Sep 17 00:00:00 2001 From: Iaroslav Zeigerman Date: Thu, 18 Jan 2018 10:25:18 -0500 Subject: [PATCH 023/173] Fix a recursive trampoline call in the Eval Defer implementation (#329) --- core/src/main/java/fj/data/Eval.java | 2 +- core/src/test/java/fj/data/EvalTest.java | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/fj/data/Eval.java b/core/src/main/java/fj/data/Eval.java index 519070a1..3e513075 100644 --- a/core/src/main/java/fj/data/Eval.java +++ b/core/src/main/java/fj/data/Eval.java @@ -232,7 +232,7 @@ private static final class DeferEval extends TrampolineEval { @Override protected final Trampoline trampoline() { - return memo._1().asTrampoline().trampoline(); + return Trampoline.suspend(P.lazy(() -> memo._1().asTrampoline().trampoline())); } } } diff --git a/core/src/test/java/fj/data/EvalTest.java b/core/src/test/java/fj/data/EvalTest.java index a604c873..a3302e55 100644 --- a/core/src/test/java/fj/data/EvalTest.java +++ b/core/src/test/java/fj/data/EvalTest.java @@ -1,9 +1,12 @@ package fj.data; import fj.F0; +import fj.F2; import org.junit.Assert; import org.junit.Test; +import java.util.Iterator; + public class EvalTest { @Test @@ -42,15 +45,19 @@ public void testAlways() { @Test public void testDefer() { // Make sure that a recursive computation is actually stack-safe. - Assert.assertEquals(even(200000).value(), "done"); - } - - private static Eval even(int n) { - return Eval.now(n <= 0).flatMap(b -> b ? Eval.now("done") : Eval.defer(() -> odd(n - 1))); + int targetValue = 200000; + Iterator it = Enumerator.intEnumerator.toStream(0).iterator(); + Eval result = foldRight(it, (v, acc) -> v == targetValue ? Eval.now(true) : acc, false); + Assert.assertTrue(result.value()); } - private static Eval odd(int n) { - return Eval.defer(() -> even(n - 1)); + private static Eval foldRight(Iterator iterator, + F2, Eval> f, + A zero) { + if (!iterator.hasNext()) { + return Eval.now(zero); + } + return f.f(iterator.next(), Eval.defer(() -> foldRight(iterator, f, zero))); } private static class InvocationTrackingF implements F0 { From 7a30ac7323670bb134b78e6ddbba4452b881af4f Mon Sep 17 00:00:00 2001 From: l1cache Date: Fri, 2 Feb 2018 11:46:12 -0800 Subject: [PATCH 024/173] Added some useful functions to Equal --- core/src/main/java/fj/Equal.java | 68 ++++++++++++++++++++++++++++ core/src/test/java/fj/EqualTest.java | 48 ++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 core/src/test/java/fj/EqualTest.java diff --git a/core/src/main/java/fj/Equal.java b/core/src/main/java/fj/Equal.java index 5e407a00..07863acf 100644 --- a/core/src/main/java/fj/Equal.java +++ b/core/src/main/java/fj/Equal.java @@ -132,6 +132,74 @@ public boolean equal(B b1, B b2) { }); } + /** + * Constructs an equal instance, which tests equality of self and the mapped object in "and" manner + * + * @param f The function to map the original object + * @param eq Equality for the mapped object + * @return A new equal instance + */ + public final Equal andThen(final F f, final Equal eq) { + return and(eq.contramap(f)); + } + + /** + * An equal instance, which executes two Equality of self in "and" manner + * + * @param eq Another equality for self + * @return A new equal instance + */ + public final Equal and(final Equal eq) { + return equalDef((a1, a2) -> def.equal(a1, a2) && eq.def.equal(a1, a2)); + } + + /** + * An equal instance, which executes two Equality of self in "or" manner + * + * @param eq Another equality for self + * @return A new equal instance + */ + public final Equal or(final Equal eq) { + return equalDef((a1, a2) -> def.equal(a1, a2) || eq.def.equal(a1, a2)); + } + + /** + * An equal instance, which reverts equality for self + * + * @return A new equal instance + */ + public final Equal not() { + return equalDef((a1, a2) -> !def.equal(a1, a2)); + } + + /** + * Static version of {@link #contramap(F)} + */ + public static Equal contramap(final F f, final Equal eq) { + return eq.contramap(f); + } + + /** + * Static version of {@link #and(Equal)} + */ + public static Equal and(final Equal eq1, final Equal eq2) { + return eq1.and(eq2); + } + + /** + * Static version of {@link #or(Equal)} + */ + public static Equal or(final Equal eq1, final Equal eq2) { + return eq1.or(eq2); + } + + /** + * Static version of {@link #not()} + */ + public static Equal not(final Equal eq) { + return eq.not(); + } + /** * Constructs an equal instance from the given function. * diff --git a/core/src/test/java/fj/EqualTest.java b/core/src/test/java/fj/EqualTest.java new file mode 100644 index 00000000..776ccc57 --- /dev/null +++ b/core/src/test/java/fj/EqualTest.java @@ -0,0 +1,48 @@ +package fj; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +public class EqualTest { + @Test + public void contramapShouldWork() { + Equal equalByLength = Equal.contramap(String::length, Equal.intEqual); + + assertThat(equalByLength.eq("str1", "str2"), is(true)); + assertThat(equalByLength.eq("str1", "str11"), is(false)); + } + + @Test + public void andShouldWork() { + Equal equalByLengthAndLastDigit = Equal.and(Equal.contramap(String::length, Equal.intEqual), + Equal.contramap(s -> s.charAt(s.length() - 1), + Equal.charEqual)); + + assertThat(equalByLengthAndLastDigit.eq("str1", "spr1"), is(true)); + assertThat(equalByLengthAndLastDigit.eq("str1", "str2"), is(false)); + assertThat(equalByLengthAndLastDigit.eq("str1", "strr1"), is(false)); + } + + @Test + public void orShouldWork() { + Equal equalByLengthOrLastDigit = Equal.or(Equal.contramap(String::length, Equal.intEqual), + Equal.contramap(s -> s.charAt(s.length() - 1), + Equal.charEqual)); + + assertThat(equalByLengthOrLastDigit.eq("str1", "str2"), is(true)); + assertThat(equalByLengthOrLastDigit.eq("str1", "strr1"), is(true)); + assertThat(equalByLengthOrLastDigit.eq("str1", "strr2"), is(false)); + } + + @Test + public void thenShouldWork() { + Equal equalByLengthThenLastDigit = Equal.contramap(String::length, Equal.intEqual) + .andThen(s -> s.charAt(s.length() - 1), Equal.charEqual); + + assertThat(equalByLengthThenLastDigit.eq("str1", "spr1"), is(true)); + assertThat(equalByLengthThenLastDigit.eq("str1", "str2"), is(false)); + assertThat(equalByLengthThenLastDigit.eq("str1", "strr1"), is(false)); + } +} From dc0f405dc9ab62c97400ce0c88853027218fb211 Mon Sep 17 00:00:00 2001 From: l1cache Date: Fri, 2 Feb 2018 13:17:49 -0800 Subject: [PATCH 025/173] few useful function added to Ord class as well --- core/src/main/java/fj/Ord.java | 33 ++++++++++++++++++++++++++++++ core/src/test/java/fj/OrdTest.java | 18 ++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/core/src/main/java/fj/Ord.java b/core/src/main/java/fj/Ord.java index a3e55edc..0b532ed1 100644 --- a/core/src/main/java/fj/Ord.java +++ b/core/src/main/java/fj/Ord.java @@ -287,6 +287,39 @@ public final Ord reverse() { return ordDef(def.dual()); } + /** + * Constructs an ord instance, which compares using self and if objects are equal compares using ord + * + * @param ord Ord for subsequent comparison + * @return A new equal instance + */ + public final Ord andThen(final Ord ord) { + return ordDef((a1, a2) -> { + final Ordering compareResult = compare(a1, a2); + if(compareResult == Ordering.EQ) + return ord.compare(a1, a2); + return compareResult; + }); + } + + /** + * Constructs an ord instance, which compares using self and if objects are equal compares the mapped objects + * + * @param f The function to map the original object + * @param ord Ord for the mapped object + * @return A new equal instance + */ + public final Ord andThen(final F f, final Ord ord) { + return andThen(ord.contramap(f)); + } + + /** + * Static version of {@link #contramap(F)} + */ + public static Ord contramap(final F f, final Ord ord) { + return ord.contramap(f); + } + /** * Returns an order instance that uses the given equality test and ordering function. * diff --git a/core/src/test/java/fj/OrdTest.java b/core/src/test/java/fj/OrdTest.java index d218387a..1dfa6223 100644 --- a/core/src/test/java/fj/OrdTest.java +++ b/core/src/test/java/fj/OrdTest.java @@ -24,4 +24,22 @@ public void isLessThan() { assertThat(pred.f(1L), is(false)); assertThat(pred.f(2L), is(false)); } + + @Test + public void contramapShouldWork() { + Ord lengthOrd = Ord.contramap(String::length, Ord.intOrd); + + assertThat(lengthOrd.compare("str", "rts"), is(Ordering.EQ)); + assertThat(lengthOrd.compare("strlong", "str"), is(Ordering.GT)); + } + + @Test + public void andThenShouldWork() { + Ord lengthThenLastDigitOrd = Ord.contramap(String::length, Ord.intOrd) + .andThen(s -> s.charAt(s.length() - 1), Ord.charOrd); + + assertThat(lengthThenLastDigitOrd.compare("str", "dyr"), is(Ordering.EQ)); + assertThat(lengthThenLastDigitOrd.compare("stt", "str"), is(Ordering.GT)); + assertThat(lengthThenLastDigitOrd.compare("str", "strr"), is(Ordering.LT)); + } } From c4f1f6830a7f6cf66efdffb9436676361af2ff25 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Tue, 20 Mar 2018 13:55:23 +0100 Subject: [PATCH 026/173] Move fluent Equal/Ord construction api to the Definition interfaces. Also remove Equal.or and Equal.and as there is no precise use case. --- core/src/main/java/fj/Equal.java | 111 +++++++++++++-------------- core/src/main/java/fj/Ord.java | 101 ++++++++++++++---------- core/src/test/java/fj/EqualTest.java | 26 +------ core/src/test/java/fj/OrdTest.java | 6 +- 4 files changed, 117 insertions(+), 127 deletions(-) diff --git a/core/src/main/java/fj/Equal.java b/core/src/main/java/fj/Equal.java index 07863acf..f427849a 100644 --- a/core/src/main/java/fj/Equal.java +++ b/core/src/main/java/fj/Equal.java @@ -48,6 +48,39 @@ public interface Definition { default boolean equal(A a1, A a2) { return equal(a1).f(a2); } + + /** + * Refine this equal definition, to tests equality of self and the mapped object in "and" manner. + * @see #equal() + * + * @param f The function to map the original object + * @param eq Equality for the mapped object + * @return A new equal definition + */ + default Definition then(final F f, final Equal eq) { + Definition bEqDef = eq.def; + return new Definition() { + @Override + public F equal(A a1) { + F fa = Definition.this.equal(a1); + F fb = bEqDef.equal(f.f(a1)); + return a2 -> fa.f(a2) && fb.f(f.f(a2)); + } + + @Override + public boolean equal(A a1, A a2) { + return Definition.this.equal(a1, a2) && bEqDef.equal(f.f(a1), f.f(a2)); + } + }; + } + + /** + * Build an equal instance from this definition. + * to be called after some successive {@link #then(F, Equal)} calls. + */ + default Equal equal() { + return equalDef(this); + } } /** @@ -118,58 +151,31 @@ public F eq(final A a) { * @return A new equal. */ public Equal contramap(final F f) { - Definition eaDef = def; - return equalDef(new Definition(){ - @Override - public F equal(B b) { - return compose(eaDef.equal(f.f(b)), f); - } - - @Override - public boolean equal(B b1, B b2) { - return eaDef.equal(f.f(b1), f.f(b2)); - } - }); + return equalDef(contramapDef(f, def)); } /** - * Constructs an equal instance, which tests equality of self and the mapped object in "and" manner + * An equal instance, which reverts equality for self * - * @param f The function to map the original object - * @param eq Equality for the mapped object * @return A new equal instance */ - public final Equal andThen(final F f, final Equal eq) { - return and(eq.contramap(f)); + public final Equal not() { + return equalDef((a1, a2) -> !def.equal(a1, a2)); } - /** - * An equal instance, which executes two Equality of self in "and" manner - * - * @param eq Another equality for self - * @return A new equal instance - */ - public final Equal and(final Equal eq) { - return equalDef((a1, a2) -> def.equal(a1, a2) && eq.def.equal(a1, a2)); - } - /** - * An equal instance, which executes two Equality of self in "or" manner - * - * @param eq Another equality for self - * @return A new equal instance - */ - public final Equal or(final Equal eq) { - return equalDef((a1, a2) -> def.equal(a1, a2) || eq.def.equal(a1, a2)); - } + private static Definition contramapDef(F f, Definition aEqDef) { + return new Definition(){ + @Override + public F equal(B b) { + return compose(aEqDef.equal(f.f(b)), f); + } - /** - * An equal instance, which reverts equality for self - * - * @return A new equal instance - */ - public final Equal not() { - return equalDef((a1, a2) -> !def.equal(a1, a2)); + @Override + public boolean equal(B b1, B b2) { + return aEqDef.equal(f.f(b1), f.f(b2)); + } + }; } /** @@ -180,24 +186,11 @@ public static Equal contramap(final F f, final Equal eq) { } /** - * Static version of {@link #and(Equal)} - */ - public static Equal and(final Equal eq1, final Equal eq2) { - return eq1.and(eq2); - } - - /** - * Static version of {@link #or(Equal)} - */ - public static Equal or(final Equal eq1, final Equal eq2) { - return eq1.or(eq2); - } - - /** - * Static version of {@link #not()} + * Begin definition of an equal instance. + * @see Definition#then(F, Equal) */ - public static Equal not(final Equal eq) { - return eq.not(); + public static Definition on(final F f, final Equal eq) { + return contramapDef(f, eq.def); } /** diff --git a/core/src/main/java/fj/Ord.java b/core/src/main/java/fj/Ord.java index 0b532ed1..0a778f4d 100644 --- a/core/src/main/java/fj/Ord.java +++ b/core/src/main/java/fj/Ord.java @@ -84,6 +84,42 @@ public Definition dual() { } }; } + + /** + * Refine this ord definition: compares using self and if objects are equal compares using given Ord. + * @see #ord() + * + * @param bOrd Ord for subsequent comparison + * @return A new ord definition. + */ + default Definition then(final F f, final Ord bOrd) { + Definition bOrdDef = bOrd.def; + return new Definition() { + @Override + public F compare(A a1) { + F fa = Definition.this.compare(a1); + F fb = bOrdDef.compare(f.f(a1)); + return a2 -> { + Ordering aOrdering = fa.f(a2); + return aOrdering != Ordering.EQ ? aOrdering : fb.f(f.f(a2)); + }; + } + + @Override + public Ordering compare(A a1, A a2) { + Ordering aOrdering = Definition.this.compare(a1, a2); + return aOrdering != Ordering.EQ ? aOrdering : bOrdDef.compare(f.f(a1), f.f(a2)); + } + }; + } + + /** + * Build an ord instance from this definition. + * to be called after some successive {@link #then(F, Ord)} calls. + */ + default Ord ord() { + return ordDef(this); + } } /** @@ -160,18 +196,7 @@ public Equal equal() { * @return A new ord. */ public Ord contramap(final F f) { - Definition selfDef = def; - return ordDef(new Definition() { - @Override - public F compare(B b) { - return compose(selfDef.compare(f.f(b)), f); - } - - @Override - public Ordering compare(B b1, B b2) { - return selfDef.compare(f.f(b1), f.f(b2)); - } - }); + return ordDef(contramapDef(f, def)); } /** @@ -288,36 +313,32 @@ public final Ord reverse() { } /** - * Constructs an ord instance, which compares using self and if objects are equal compares using ord - * - * @param ord Ord for subsequent comparison - * @return A new equal instance + * Begin definition of an ord instance. + * @see Definition#then(F, Equal) */ - public final Ord andThen(final Ord ord) { - return ordDef((a1, a2) -> { - final Ordering compareResult = compare(a1, a2); - if(compareResult == Ordering.EQ) - return ord.compare(a1, a2); - return compareResult; - }); - } - - /** - * Constructs an ord instance, which compares using self and if objects are equal compares the mapped objects - * - * @param f The function to map the original object - * @param ord Ord for the mapped object - * @return A new equal instance - */ - public final Ord andThen(final F f, final Ord ord) { - return andThen(ord.contramap(f)); + public static Definition on(final F f, final Ord ord) { + return contramapDef(f, ord.def); } /** * Static version of {@link #contramap(F)} */ public static Ord contramap(final F f, final Ord ord) { - return ord.contramap(f); + return ordDef(contramapDef(f, ord.def)); + } + + private static Definition contramapDef(F f, Definition def) { + return new Definition() { + @Override + public F compare(B b) { + return compose(def.compare(f.f(b)), f); + } + + @Override + public Ordering compare(B b1, B b2) { + return def.compare(f.f(b1), f.f(b2)); + } + }; } /** @@ -609,15 +630,15 @@ public static Ord> p1Ord(final Ord oa) { * @return An order instance for a product-2, with the first factor considered most significant. */ public static Ord> p2Ord(final Ord oa, final Ord ob) { - return ordDef((a, b) -> oa.eq(a._1(), b._1()) ? ob.compare(a._2(), b._2()) : oa.compare(a._1(), b._1())); + return on(P2.__1(), oa).then(P2.__2(), ob).ord(); } public static Ord> p2Ord1(Ord oa) { - return ordDef((p1, p2) -> oa.compare(p1._1(), p2._1())); + return on(P2.__1(), oa).ord(); } public static Ord> p2Ord2(Ord ob) { - return ordDef((p1, p2) -> ob.compare(p1._2(), p2._2())); + return on(P2.__2(), ob).ord(); } /** @@ -629,9 +650,7 @@ public static Ord> p2Ord2(Ord ob) { * @return An order instance for a product-3, with the first factor considered most significant. */ public static Ord> p3Ord(final Ord oa, final Ord ob, final Ord oc) { - return ordDef((a, b) -> oa.eq(a._1(), b._1()) ? - p2Ord(ob, oc).compare(P.p(a._2(), a._3()), P.p(b._2(), b._3())) - : oa.compare(a._1(), b._1())); + return on(P3.__1(), oa).then(P3.__2(), ob).then(P3.__3(), oc).ord(); } /** diff --git a/core/src/test/java/fj/EqualTest.java b/core/src/test/java/fj/EqualTest.java index 776ccc57..bacecc46 100644 --- a/core/src/test/java/fj/EqualTest.java +++ b/core/src/test/java/fj/EqualTest.java @@ -14,32 +14,10 @@ public void contramapShouldWork() { assertThat(equalByLength.eq("str1", "str11"), is(false)); } - @Test - public void andShouldWork() { - Equal equalByLengthAndLastDigit = Equal.and(Equal.contramap(String::length, Equal.intEqual), - Equal.contramap(s -> s.charAt(s.length() - 1), - Equal.charEqual)); - - assertThat(equalByLengthAndLastDigit.eq("str1", "spr1"), is(true)); - assertThat(equalByLengthAndLastDigit.eq("str1", "str2"), is(false)); - assertThat(equalByLengthAndLastDigit.eq("str1", "strr1"), is(false)); - } - - @Test - public void orShouldWork() { - Equal equalByLengthOrLastDigit = Equal.or(Equal.contramap(String::length, Equal.intEqual), - Equal.contramap(s -> s.charAt(s.length() - 1), - Equal.charEqual)); - - assertThat(equalByLengthOrLastDigit.eq("str1", "str2"), is(true)); - assertThat(equalByLengthOrLastDigit.eq("str1", "strr1"), is(true)); - assertThat(equalByLengthOrLastDigit.eq("str1", "strr2"), is(false)); - } - @Test public void thenShouldWork() { - Equal equalByLengthThenLastDigit = Equal.contramap(String::length, Equal.intEqual) - .andThen(s -> s.charAt(s.length() - 1), Equal.charEqual); + Equal equalByLengthThenLastDigit = Equal.on(String::length, Equal.intEqual) + .then(s -> s.charAt(s.length() - 1), Equal.charEqual).equal(); assertThat(equalByLengthThenLastDigit.eq("str1", "spr1"), is(true)); assertThat(equalByLengthThenLastDigit.eq("str1", "str2"), is(false)); diff --git a/core/src/test/java/fj/OrdTest.java b/core/src/test/java/fj/OrdTest.java index 1dfa6223..4addec65 100644 --- a/core/src/test/java/fj/OrdTest.java +++ b/core/src/test/java/fj/OrdTest.java @@ -34,9 +34,9 @@ public void contramapShouldWork() { } @Test - public void andThenShouldWork() { - Ord lengthThenLastDigitOrd = Ord.contramap(String::length, Ord.intOrd) - .andThen(s -> s.charAt(s.length() - 1), Ord.charOrd); + public void thenShouldWork() { + Ord lengthThenLastDigitOrd = Ord.on(String::length, Ord.intOrd) + .then(s -> s.charAt(s.length() - 1), Ord.charOrd).ord(); assertThat(lengthThenLastDigitOrd.compare("str", "dyr"), is(Ordering.EQ)); assertThat(lengthThenLastDigitOrd.compare("stt", "str"), is(Ordering.GT)); From bbc28c47a29d04724007192100edef79499078cc Mon Sep 17 00:00:00 2001 From: Sidney Quitorio Date: Tue, 3 Jul 2018 16:16:17 -0700 Subject: [PATCH 027/173] Fixes exception in Either.LeftProjection.traverseIO --- core/src/main/java/fj/data/Either.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/fj/data/Either.java b/core/src/main/java/fj/data/Either.java index 7d8e0bb7..a9a746a9 100644 --- a/core/src/main/java/fj/data/Either.java +++ b/core/src/main/java/fj/data/Either.java @@ -316,7 +316,7 @@ public List> traverseList(final F> f) { * @return An either after traversing through this projection. */ public IO> traverseIO(final F> f) { - return e.isRight() ? + return e.isLeft() ? IOFunctions.map(f.f(value()), Either::left) : IOFunctions.unit(Either.right(e.right().value())); } From 8ff163c6bbcaf4888bde7dcef21a16137b4db5c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Wed, 11 Jul 2018 21:24:12 -0400 Subject: [PATCH 028/173] Bring Gradle to 4.8.1 --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f16d2666..717f0389 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip diff --git a/gradlew b/gradlew index 4453ccea..cccdd3d5 100755 --- a/gradlew +++ b/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } From 4b6536110719689dad7e784ac389db05a0959af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Wed, 11 Jul 2018 21:43:15 -0400 Subject: [PATCH 029/173] Bring dependencies current MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- build.gradle | 2 +- core/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 6f8ecc5d..e1fed096 100644 --- a/build.gradle +++ b/build.gradle @@ -72,7 +72,7 @@ allprojects { sonatypeUploadUrl = isSnapshot ? sonatypeSnapshotUrl : sonatypeReleaseUrl primaryEmail = "functionaljava@googlegroups.com" - dependencyJunit = "junit:junit:4.12" + dependencyJunit = "org.junit.vintage:junit-vintage-engine:5.2.0" displayCompilerWarnings = true diff --git a/core/build.gradle b/core/build.gradle index e0c86f0c..71dae913 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -7,7 +7,7 @@ archivesBaseName = project.projectName dependencies { testCompile dependencyJunit - testCompile 'com.h2database:h2:1.4.196' + testCompile 'com.h2database:h2:1.4.197' testCompile 'commons-dbutils:commons-dbutils:1.7' } From 7576f6f65537cbb7f059d683d96425860b306213 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sat, 21 Jul 2018 09:21:05 +0200 Subject: [PATCH 030/173] use openjdk7, remove oracle installer, try sudo false --- .travis.yml | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4a9f2d24..539e7a45 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,30 +1,13 @@ -language: java +# Use faster, Docker-based container (instead of OpenVZ) +sudo: false -sudo: required +language: java jdk: - oraclejdk8 -addons: - apt: - packages: - - oracle-java8-installer - -before_script: - - sudo service postgresql stop || true - - sudo service mysql stop || true - - sudo service memcached stop || true - - sudo service bootlogd stop || true - - sudo service elasticsearch stop || true - - sudo service mongodb stop || true - - sudo service neo4j stop || true - - sudo service cassandra stop || true - - sudo service riak stop || true - - sudo service rsync stop || true - - sudo service x11-common stop || true - script: - - jdk_switcher use oraclejdk7 && export JAVA7_HOME=$JAVA_HOME + - jdk_switcher use openjdk7 && export JAVA7_HOME=$JAVA_HOME - jdk_switcher use oraclejdk8 && export JAVA8_HOME=$JAVA_HOME - ./gradlew build coverage -s -i From b280c531debd92885ca6b7998cb2aa485a0fd955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Thu, 26 Jul 2018 18:48:45 -0400 Subject: [PATCH 031/173] Add IOFunctions tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/test/java/fj/IOTest.java | 30 -------- .../test/java/fj/data/IOFunctionsTest.java | 69 +++++++++++++++++-- 2 files changed, 63 insertions(+), 36 deletions(-) delete mode 100644 core/src/test/java/fj/IOTest.java diff --git a/core/src/test/java/fj/IOTest.java b/core/src/test/java/fj/IOTest.java deleted file mode 100644 index f965204c..00000000 --- a/core/src/test/java/fj/IOTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package fj; - -import fj.data.IO; -import fj.data.IOFunctions; -import org.junit.Test; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.StringReader; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - - -public class IOTest { - - @Test - public void testLift() throws IOException { - final IO readName = () -> new BufferedReader(new StringReader("foo")).readLine(); - final F> upperCaseAndPrint = F1Functions., String>o(this::println).f(String::toUpperCase); - final IO readAndPrintUpperCasedName = IOFunctions.bind(readName, upperCaseAndPrint); - assertThat(readAndPrintUpperCasedName.run(), is("FOO")); - } - - public IO println(final String s) { - return () -> { - return s; - }; - } -} diff --git a/core/src/test/java/fj/data/IOFunctionsTest.java b/core/src/test/java/fj/data/IOFunctionsTest.java index dcafc221..b943db36 100644 --- a/core/src/test/java/fj/data/IOFunctionsTest.java +++ b/core/src/test/java/fj/data/IOFunctionsTest.java @@ -1,7 +1,6 @@ package fj.data; -import fj.Unit; -import org.hamcrest.core.Is; +import fj.*; import org.junit.Assert; import org.junit.Test; @@ -9,6 +8,10 @@ import java.io.Reader; import java.util.concurrent.atomic.AtomicBoolean; +import static fj.data.IOFunctions.*; +import static fj.data.Stream.cons; +import static fj.data.Stream.nil_; +import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; public class IOFunctionsTest { @@ -30,8 +33,8 @@ public void close() { r -> () -> new BufferedReader(r).readLine() ); - Assert.assertThat(bracketed.run(), Is.is("Read OK")); - Assert.assertThat(closed.get(), Is.is(true)); + Assert.assertThat(bracketed.run(), is("Read OK")); + Assert.assertThat(closed.get(), is(true)); } @Test @@ -56,9 +59,63 @@ public void close() { bracketed.run(); fail("Exception expected"); } catch (IllegalArgumentException e) { - Assert.assertThat(e.getMessage(), Is.is("OoO")); + Assert.assertThat(e.getMessage(), is("OoO")); } - Assert.assertThat(closed.get(), Is.is(true)); + Assert.assertThat(closed.get(), is(true)); + } + + @Test + public void testTraverseIO() throws IOException { + String[] as = {"foo1", "bar2", "foobar3"}; + Stream stream = Stream.arrayStream(as); + ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + PrintStream originalOut = System.out; + System.setOut(new PrintStream(outContent)); + stream.traverseIO(IOFunctions::stdoutPrint).run(); + System.setOut(originalOut); + assertThat(outContent.toString(), is("foobar3bar2foo1")); + } + + @Test + public void testSequenceWhile() throws IOException { + BufferedReader r = new BufferedReader(new StringReader("foo1\nbar2\nfoobar3")); + Stream> s1 = Stream.repeat(() -> r.readLine()); + IO> io = sequenceWhile(s1, s -> !s.equals("foobar3")); + assertThat(io.run(), is(cons("foo1", () -> cons("bar2", () -> Stream.nil())))); + } + + @Test + public void testForeach() throws IOException { + Stream> s1 = Stream.repeat(() -> "foo1"); + IO> io = sequence(s1.take(2)); + ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + PrintStream originalOut = System.out; + System.setOut(new PrintStream(outContent)); + runSafe(io).foreach(s -> runSafe(stdoutPrint(s))); + System.setOut(originalOut); + assertThat(outContent.toString(), is("foo1foo1")); + } + + + @Test + public void testReplicateM() throws IOException { + final IO is = () -> new BufferedReader(new StringReader("foo")).readLine(); + assertThat(replicateM(is, 3).run(), is(List.list("foo", "foo", "foo"))); + } + + + @Test + public void testLift() throws IOException { + final IO readName = () -> new BufferedReader(new StringReader("foo")).readLine(); + final F> upperCaseAndPrint = F1Functions., String>o(this::println).f(String::toUpperCase); + final IO readAndPrintUpperCasedName = IOFunctions.bind(readName, upperCaseAndPrint); + assertThat(readAndPrintUpperCasedName.run(), is("FOO")); + } + + private IO println(final String s) { + return () -> { + return s; + }; } } \ No newline at end of file From be646621a7be6dc7dfe5a10bb9fd26fd3d04c6e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sun, 29 Jul 2018 08:47:35 -0400 Subject: [PATCH 032/173] Add Stream tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/test/java/fj/data/StreamTest.java | 75 ++++++++++++++++++++-- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/core/src/test/java/fj/data/StreamTest.java b/core/src/test/java/fj/data/StreamTest.java index a37764c2..49840b78 100644 --- a/core/src/test/java/fj/data/StreamTest.java +++ b/core/src/test/java/fj/data/StreamTest.java @@ -1,16 +1,16 @@ package fj.data; +import fj.Equal; +import fj.Ord; +import fj.P2; +import fj.control.parallel.Strategy; import org.junit.Test; -import java.io.IOException; -import java.util.ArrayList; import java.util.ConcurrentModificationException; -import static fj.data.IOFunctions.stdinReadLine; -import static java.lang.System.out; +import static fj.data.Stream.*; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; /** @@ -21,7 +21,7 @@ public class StreamTest { @Test public void infiniteStream() { Stream s = Stream.forever(Enumerator.intEnumerator, 0).bind(Stream::single); - assertEquals(List.range(0, 5), s.take(5).toList()); + assertThat(List.range(0, 5), is(s.take(5).toList())); } @Test @@ -48,5 +48,68 @@ public void iterableStreamWithStructureUpdate() { x = s2.head(); } + @Test(expected=Error.class) + public void testCycleNil(){ + cycle(Stream.nil()); + } + + @Test + public void testCycle() { + Stream s = stream(new Character[]{'a', 'b'}); + assertThat(cycle(s).take(5), + is(stream(new Character[]{'a', 'b', 'a', 'b', 'a'}))); + } + + @Test + public void testIterate() { + assertThat(iterate(a -> 2 * a + 1, 1).take(5), + is(stream(new Integer[]{1, 3, 7, 15, 31}))); + } + + @Test + public void testArrayStreamEmpty() { + assertThat(arrayStream(new Integer[]{}), is(Stream.nil())); + } + + @Test(expected=Error.class) + public void testNilHead() { + Stream.nil().head(); + } + @Test(expected=Error.class) + public void testNilTail() { + Stream.nil().tail(); + } + + @Test + public void testArray() { + Character[] a = new Character[]{'a', 'b', 'c'}; + Stream s = stream(a); + assertThat(s.array(Character[].class), is(a)); + } + + @Test + public void testZipIndex() { + Character[] a = new Character[]{'a', 'b', 'c'}; + P2, Stream> p = unzip(stream(a).zipIndex().drop(1)); + assertThat(p._1(), is(stream(new Character[]{'b', 'c'}))); + assertThat(p._2(), is(stream(new Integer[]{1, 2}))); + } + + @Test + public void testMinus() { + Character[] a1 = new Character[]{'a', 'b', 'c', 'd', 'e'}; + Stream s1 = stream(a1); + Character[] a2 = new Character[]{'b', 'e'}; + Stream s2 = stream(a2); + assertThat(s1.minus(Equal.charEqual, s2), + is(stream(new Character[]{'a', 'c', 'd'}))); + } + + @Test + public void testSort() { + Stream s = range(Enumerator.intEnumerator, 99, -99, -1); + assertThat(s.sort(Ord.intOrd, Strategy.seqStrategy()), + is(s.sort(Ord.intOrd))); + } } From 99a7642a5fbae7e90e5ce80bbe44842acc8f524b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 29 Jul 2018 21:20:43 +0200 Subject: [PATCH 033/173] Update to latest retrolambda --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index e1fed096..08d3e946 100644 --- a/build.gradle +++ b/build.gradle @@ -7,8 +7,8 @@ ext { buildscript { ext { uptodateVersion = "1.6.3" - retrolambdaPluginVersion = "3.5.0" - retrolambdaVersion = "2.5.1" + retrolambdaPluginVersion = "3.7.0" + retrolambdaVersion = "2.5.4" } repositories { From 3fc4ab2994d942c121d79be37bda672e9a2a8262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sun, 29 Jul 2018 17:01:47 -0400 Subject: [PATCH 034/173] Exclude paths in codecov.io MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- codecov.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 codecov.yml diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..8c7fd5e4 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,3 @@ +ignore: + - "demo/.*" + - "performance/.*" \ No newline at end of file From fa322e27df3c491d2e771a540894f9073d95f6eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Mon, 30 Jul 2018 15:45:03 -0400 Subject: [PATCH 035/173] Bring Gradle Jacoco current MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index e1fed096..1cc8078e 100644 --- a/build.gradle +++ b/build.gradle @@ -38,8 +38,7 @@ allprojects { jacoco { -// toolVersion = "0.7.1.201405082137" - toolVersion = "0.7.6.201602180812" + toolVersion = "0.8.1" } From e9fd4eca665bf392c6b1a72176e476ff6bae1289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Mon, 30 Jul 2018 15:22:45 -0400 Subject: [PATCH 036/173] Add tests for Try, F, FW, Digit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/main/java/fj/F8Functions.java | 1 - core/src/test/java/fj/DigitTest.java | 30 ++++++++ core/src/test/java/fj/FFunctionsTest.java | 25 +++++++ core/src/test/java/fj/FWFunctionsTest.java | 24 ++++++ core/src/test/java/fj/ShowTest.java | 6 -- core/src/test/java/fj/TryEffectTest.java | 82 +++++++++++++++++++++ core/src/test/java/fj/TryTest.java | 50 +++++++++++++ core/src/test/java/fj/data/JavaTest.java | 4 +- core/src/test/java/fj/data/OptionTest.java | 1 - core/src/test/java/fj/data/SeqTest.java | 5 +- core/src/test/java/fj/data/StreamTest.java | 9 ++- core/src/test/java/fj/data/TreeMapTest.java | 11 --- 12 files changed, 226 insertions(+), 22 deletions(-) create mode 100644 core/src/test/java/fj/DigitTest.java create mode 100644 core/src/test/java/fj/FFunctionsTest.java create mode 100644 core/src/test/java/fj/FWFunctionsTest.java create mode 100644 core/src/test/java/fj/TryEffectTest.java create mode 100644 core/src/test/java/fj/TryTest.java diff --git a/core/src/main/java/fj/F8Functions.java b/core/src/main/java/fj/F8Functions.java index 071cb6bc..8b9883a3 100644 --- a/core/src/main/java/fj/F8Functions.java +++ b/core/src/main/java/fj/F8Functions.java @@ -11,7 +11,6 @@ private F8Functions() { /** * Partial application. * - * @param a The A to which to apply this function. * @return The function partially applied to the given argument. */ public static F7 f(final F8 func, final A a) { diff --git a/core/src/test/java/fj/DigitTest.java b/core/src/test/java/fj/DigitTest.java new file mode 100644 index 00000000..9fc98d4c --- /dev/null +++ b/core/src/test/java/fj/DigitTest.java @@ -0,0 +1,30 @@ +package fj; + +import fj.data.Option; +import org.junit.Test; + +import static fj.data.Array.range; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class DigitTest { + @Test + public void testInteger() { + for (Integer i: range(0, 10)) { + assertThat(Digit.fromLong(i).toLong(), is(i.longValue())); + } + } + + @Test + public void testChar() { + for (Integer i: range(0, 10)) { + Character c = Character.forDigit(i, 10); + assertThat(Digit.fromChar(c).some().toChar(), is(c)); + } + } + + @Test + public void testCharNone() { + assertThat(Digit.fromChar('x'), is(Option.none())); + } +} diff --git a/core/src/test/java/fj/FFunctionsTest.java b/core/src/test/java/fj/FFunctionsTest.java new file mode 100644 index 00000000..9d38e1f5 --- /dev/null +++ b/core/src/test/java/fj/FFunctionsTest.java @@ -0,0 +1,25 @@ +package fj; + +import org.junit.Test; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.*; + +public class FFunctionsTest { + @Test + public void testApply() { + F8 f8 = + (i1, i2, i3, i4, i5, i6, i7, i8) -> + i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8; + F7 f7 = F8Functions.f(f8, 8); + F6 f6 = + F7Functions.f(f7, 7); + F5 f5 = F6Functions.f(f6, 6); + F4 f4 = F5Functions.f(f5, 5); + F3 f3 = F4Functions.f(f4, 4); + F2 f2 = F3Functions.f(f3, 3); + F f1 = F2Functions.f(f2, 2); + assertThat(F1Functions.f(f1, 1).f(), is(36)); + } +} diff --git a/core/src/test/java/fj/FWFunctionsTest.java b/core/src/test/java/fj/FWFunctionsTest.java new file mode 100644 index 00000000..b9ce65d5 --- /dev/null +++ b/core/src/test/java/fj/FWFunctionsTest.java @@ -0,0 +1,24 @@ +package fj; + +import org.junit.Test; + +import fj.F1W; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class FWFunctionsTest { + @Test + public void testLift1() { + F f = i -> i + 1; + F1W f1w = F1W.lift(f); + assertThat(f1w.f(1), is(2)); + } + + @Test + public void testLift2() { + F2 f2 = (i, j) -> i + j; + F2W f2w = F2W.lift(f2); + assertThat(f2w.f(1, 2), is(3)); + } + +} diff --git a/core/src/test/java/fj/ShowTest.java b/core/src/test/java/fj/ShowTest.java index e271f5a1..c516c0df 100644 --- a/core/src/test/java/fj/ShowTest.java +++ b/core/src/test/java/fj/ShowTest.java @@ -10,16 +10,10 @@ * Created by MarkPerry on 4/06/2015. */ public class ShowTest { - - - @Test public void arrayShow() { Array a = array(3, 5, 7); String s = Show.arrayShow(Show.intShow).showS(a); - System.out.println(s); assertTrue(s.equals("Array(3,5,7)")); - } - } diff --git a/core/src/test/java/fj/TryEffectTest.java b/core/src/test/java/fj/TryEffectTest.java new file mode 100644 index 00000000..bb684fc6 --- /dev/null +++ b/core/src/test/java/fj/TryEffectTest.java @@ -0,0 +1,82 @@ +package fj; + +import fj.data.Validation; +import fj.function.TryEffect0; +import fj.function.TryEffect1; +import org.junit.Test; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.*; + +public class TryEffectTest { + + @Test + public void testTryEffect0Success() { + F> f = + TryEffect.f(TryEffect0::f); + Validation v = f.f(new AlwaysSucceed0()); + assertThat(v.isSuccess(), is(true)); + assertThat(v.success(), is(Unit.unit())); + } + + @Test + public void testTryEffect0Fail() { + F> f = + TryEffect.f(TryEffect0::f); + Validation v = f.f(new AlwaysFail0()); + assertThat(v.isFail(), is(true)); + assertThat(v.fail(), is(new TryEffectException())); + } + + @Test + public void testTryEffect1Success() { + F2, Integer, Validation> f = + TryEffect.f(TryEffect1::f); + Validation v = f.f(new AlwaysSucceed1(), 1); + assertThat(v.isSuccess(), is(true)); + assertThat(v.success(), is(Unit.unit())); + } + + @Test + public void testTryEffect1Fail() { + F2, Integer, Validation> f = + TryEffect.f(TryEffect1::f); + Validation v = f.f(new AlwaysFail1(), 1); + assertThat(v.isFail(), is(true)); + assertThat(v.fail(), is(new TryEffectException())); + } + + class AlwaysSucceed0 implements TryEffect0 { + @Override + public void f() throws TryEffectException { + // SUCCESS + } + } + + class AlwaysSucceed1 implements TryEffect1 { + @Override + public void f(Integer i) throws TryEffectException { + // SUCCESS; + } + } + + class AlwaysFail0 implements TryEffect0 { + @Override + public void f() throws TryEffectException { + throw new TryEffectException(); + } + } + + class AlwaysFail1 implements TryEffect1 { + @Override + public void f(Integer i) throws TryEffectException { + throw new TryEffectException(); + } + } + + class TryEffectException extends Exception { + @Override + public boolean equals (Object obj) { + return (obj instanceof TryEffectException); + } + } +} diff --git a/core/src/test/java/fj/TryTest.java b/core/src/test/java/fj/TryTest.java new file mode 100644 index 00000000..bd94e986 --- /dev/null +++ b/core/src/test/java/fj/TryTest.java @@ -0,0 +1,50 @@ +package fj; + +import fj.data.Validation; +import fj.function.Try0; +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class TryTest { + + @Test + public void testTrySuccess() { + F, Validation> f = + Try.f(Try0::f); + Validation v = f.f(new AlwaysSucceed()); + assertThat(v.isSuccess(), is(true)); + assertThat(v.success(), is(99)); + } + + @Test + public void testTryFail() { + F, Validation> f = + Try.f(Try0::f); + Validation v = f.f(new AlwaysFail()); + assertThat(v.isFail(), is(true)); + assertThat(v.fail(), is(new TryException())); + } + + class AlwaysSucceed implements Try0 { + @Override + public Integer f() throws TryException { + return 99; + } + } + + class AlwaysFail implements Try0 { + @Override + public Integer f() throws TryException { + throw new TryException(); + } + } + + class TryException extends Exception { + @Override + public boolean equals (Object obj) { + return (obj instanceof TryException); + } + } +} diff --git a/core/src/test/java/fj/data/JavaTest.java b/core/src/test/java/fj/data/JavaTest.java index 3b5a5785..2a9b6bfd 100644 --- a/core/src/test/java/fj/data/JavaTest.java +++ b/core/src/test/java/fj/data/JavaTest.java @@ -6,6 +6,8 @@ import java.util.EnumSet; import static fj.Show.listShow; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; /** * Created by MarkPerry on 14/07/2014. @@ -16,7 +18,7 @@ public class JavaTest { public void test1() { // #33: Fixes ClassCastException final List colors = Java.EnumSet_List().f(EnumSet.allOf(Colors.class)); - listShow(Show.anyShow()).print(colors); + assertThat(listShow(Show.anyShow()).showS(colors), is("List(red,green,blue)")); } enum Colors { diff --git a/core/src/test/java/fj/data/OptionTest.java b/core/src/test/java/fj/data/OptionTest.java index 5d2778e5..3eeed489 100644 --- a/core/src/test/java/fj/data/OptionTest.java +++ b/core/src/test/java/fj/data/OptionTest.java @@ -23,7 +23,6 @@ public void traverseList() { int max = 3; List> actual = some(max).traverseList(a -> List.range(1, a + 1)); List> expected = List.range(1, max + 1).map(i -> some(i)); - System.out.println(String.format("actual: %s, expected: %s", actual.toString(), expected.toString())); assertTrue(actual.equals(expected)); } diff --git a/core/src/test/java/fj/data/SeqTest.java b/core/src/test/java/fj/data/SeqTest.java index 27ec26e2..a7c85175 100644 --- a/core/src/test/java/fj/data/SeqTest.java +++ b/core/src/test/java/fj/data/SeqTest.java @@ -3,6 +3,8 @@ import fj.P2; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -40,7 +42,8 @@ public void convertToString() { @Test public void test() { P2, Seq> p2 = Seq.single(1).split(5); - System.out.println(p2); + assertThat(p2._1(), is(Seq.single(1))); + assertThat(p2._2(), is(Seq.empty())); } } diff --git a/core/src/test/java/fj/data/StreamTest.java b/core/src/test/java/fj/data/StreamTest.java index 49840b78..b65e115c 100644 --- a/core/src/test/java/fj/data/StreamTest.java +++ b/core/src/test/java/fj/data/StreamTest.java @@ -107,9 +107,16 @@ public void testMinus() { } @Test - public void testSort() { + public void testSortSeq() { Stream s = range(Enumerator.intEnumerator, 99, -99, -1); assertThat(s.sort(Ord.intOrd, Strategy.seqStrategy()), is(s.sort(Ord.intOrd))); } + + @Test + public void testSortThread() { + Stream s = range(Enumerator.intEnumerator, 99, -99, -1); + assertThat(s.sort(Ord.intOrd, Strategy.simpleThreadStrategy()), + is(s.sort(Ord.intOrd))); + } } diff --git a/core/src/test/java/fj/data/TreeMapTest.java b/core/src/test/java/fj/data/TreeMapTest.java index d3e60936..96cb95b2 100644 --- a/core/src/test/java/fj/data/TreeMapTest.java +++ b/core/src/test/java/fj/data/TreeMapTest.java @@ -38,10 +38,6 @@ public void split() { Show> ss = Show.setShow(Show.stringShow); Show> so = Show.optionShow(Show.stringShow); Show, Option, Set>> sp3 = Show.p3Show(ss, so, ss); - if (true) { - st.println(m2); - sp3.println(p); - } // assert equals Equal> seq = Equal.setEqual(Equal.stringEqual); @@ -70,13 +66,6 @@ public void splitLookup() { List rightList = List.range(pivot + 1, max + 1); TreeMap rightMap = iterableTreeMap(Ord.intOrd, rightList.zip(rightList.map(i -> i.toString()))); - // debug info - if (true) { - Show> st = Show.treeMapShow(Show.intShow, Show.stringShow); - Show, Option, TreeMap>> sp3 = Show.p3Show(st, Show.optionShow(Show.stringShow), st); - sp3.println(p3); - } - // do the assert Equal> tme = Equal.treeMapEqual(Equal.intEqual, Equal.stringEqual); Equal, Option, TreeMap>> eq = Equal.p3Equal(tme, Equal.optionEqual(Equal.stringEqual), tme); From ae70d20b6370531dca2efcd576eefbeb3e03228a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sun, 29 Jul 2018 14:07:30 -0400 Subject: [PATCH 037/173] Implement Zipper Eq and Hash and add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/main/java/fj/Equal.java | 30 +++--- core/src/main/java/fj/Hash.java | 20 ++++ core/src/main/java/fj/data/Zipper.java | 23 +++-- core/src/test/java/fj/data/ZipperTest.java | 110 +++++++++++++++++++++ 4 files changed, 157 insertions(+), 26 deletions(-) create mode 100644 core/src/test/java/fj/data/ZipperTest.java diff --git a/core/src/main/java/fj/Equal.java b/core/src/main/java/fj/Equal.java index f427849a..9cf2a72a 100644 --- a/core/src/main/java/fj/Equal.java +++ b/core/src/main/java/fj/Equal.java @@ -1,19 +1,6 @@ package fj; -import fj.data.Array; -import fj.data.Either; -import fj.data.LazyString; -import fj.data.List; -import fj.data.Natural; -import fj.data.NonEmptyList; -import fj.data.Option; -import fj.data.Seq; -import fj.data.Set; -import fj.data.Stream; -import fj.data.Tree; -import fj.data.TreeMap; -import fj.data.Validation; -import fj.data.Writer; +import fj.data.*; import fj.data.hamt.BitSet; import fj.data.hlist.HList; import fj.data.vector.V2; @@ -455,6 +442,21 @@ public static Equal> streamEqual(final Equal ea) { }); } + /** + * An equal instance for the {@link Zipper} type. + * + * @param ea Equality across the elements of the zipper. + * @return An equal instance for the {@link Zipper} type. + */ + public static Equal> zipperEqual(final Equal ea) { + Equal> se = Equal.streamEqual(ea); + return equalDef((a1, a2) -> + se.eq(a1.lefts(), a2.lefts()) && + ea.eq(a1.focus(), a2.focus()) && + se.eq(a1.rights(), a2.rights()) + ); + } + /** * An equal instance for the {@link Array} type. * diff --git a/core/src/main/java/fj/Hash.java b/core/src/main/java/fj/Hash.java index 14f8a9d7..708fe853 100644 --- a/core/src/main/java/fj/Hash.java +++ b/core/src/main/java/fj/Hash.java @@ -262,6 +262,26 @@ public static Hash> arrayHash(final Hash ha) { }); } + /** + * A hash instance for the {@link Zipper} type. + * + * @param ha A hash for the elements of the zipper. + * @return A hash instance for the {@link Zipper} type. + */ + public static Hash> zipperHash(final Hash ha) { + Hash> sh = streamHash(ha); + return hash(as -> { + final int p = 419; + int r = 239; + + r = p * r + sh.hash(as.lefts()); + r = p * r + ha.hash(as.focus()); + r = p * r + sh.hash(as.rights()); + + return r; + }); + } + /** * A hash instance for the {@link Tree} type. * diff --git a/core/src/main/java/fj/data/Zipper.java b/core/src/main/java/fj/data/Zipper.java index 24414ed2..2514d8e2 100644 --- a/core/src/main/java/fj/data/Zipper.java +++ b/core/src/main/java/fj/data/Zipper.java @@ -1,17 +1,6 @@ package fj.data; -import fj.Equal; -import fj.F; -import fj.F2; -import fj.F2Functions; -import fj.F3; -import fj.Function; -import fj.Ord; -import fj.P; -import fj.P1; -import fj.P2; -import fj.P3; -import fj.Show; +import fj.*; import fj.function.Integers; import java.util.Iterator; @@ -106,6 +95,16 @@ public static Ord> ord(final Ord o) { return Ord.p3Ord(so, o, so).contramap(Zipper.p_()); } + @Override + public final boolean equals(Object other) { + return Equal.equals0(Zipper.class, this, other, () -> Equal.zipperEqual(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.zipperHash(Hash.anyHash()).hash(this); + } + /** * An Equal instance for Zippers. * diff --git a/core/src/test/java/fj/data/ZipperTest.java b/core/src/test/java/fj/data/ZipperTest.java new file mode 100644 index 00000000..51c7b4ea --- /dev/null +++ b/core/src/test/java/fj/data/ZipperTest.java @@ -0,0 +1,110 @@ +package fj.data; + +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class ZipperTest { + @Test + public void testZipper() { + Zipper z = Zipper.zipper(Stream.nil(), 0, Stream.range(1, 9)); + assertThat(z.map(i -> i + 13).toStream(), is(Stream.range(13, 22))); + } + + @Test + public void testNext() { + Zipper z = Zipper.zipper(Stream.single(1), 2, Stream.single(3)); + z = z.next().some(); + assertThat(z.lefts(), is(Stream.arrayStream(new Integer[]{2, 1}))); + assertThat(z.focus(), is(3)); + assertThat(z.rights(), is(Stream.nil())); + } + + @Test + public void testNextNone() { + Zipper z = Zipper.zipper(Stream.single(1), 2, Stream.nil()); + assertThat(z.next().isNone(), is(true)); + } + + @Test + public void testCycleNext() { + Zipper z = Zipper.zipper(Stream.single(1), 2, Stream.single(3)); + assertThat(z.cycleNext(), is(z.next().some())); + } + + @Test + public void testCycleNextLast() { + Zipper z = Zipper.zipper(Stream.single(1), 2, Stream.nil()); + z = z.cycleNext(); + assertThat(z.lefts(), is(Stream.nil())); + assertThat(z.focus(), is(1)); + assertThat(z.rights(), is(Stream.single(2))); + } + + @Test + public void testPrevious() { + Zipper z = Zipper.zipper(Stream.single(1), 2, Stream.single(3)); + z = z.previous().some(); + assertThat(z.lefts(), is(Stream.nil())); + assertThat(z.focus(), is(1)); + assertThat(z.rights(), is(Stream.arrayStream(new Integer[]{2, 3}))); + } + + @Test + public void testPreviousNone() { + Zipper z = Zipper.zipper(Stream.nil(), 2, Stream.single(3)); + assertThat(z.previous().isNone(), is(true)); + } + + @Test + public void testCyclePrevious() { + Zipper z = Zipper.zipper(Stream.single(1), 2, Stream.single(3)); + assertThat(z.cyclePrevious(), is(z.previous().some())); + } + + @Test + public void testCyclePreviousFirst() { + Zipper z = Zipper.zipper(Stream.nil(), 1, Stream.single(2)); + z = z.cyclePrevious(); + assertThat(z.lefts(), is(Stream.single(1))); + assertThat(z.focus(), is(2)); + assertThat(z.rights(), is(Stream.nil())); + } + + @Test + public void testInsertLeft() { + Zipper z = Zipper.single(2); + z = z.insertLeft(1); + assertThat(z.lefts(), is(Stream.nil())); + assertThat(z.focus(), is(1)); + assertThat(z.rights(), is(Stream.single(2))); + } + + @Test + public void testInsertRight() { + Zipper z = Zipper.single(2); + z = z.insertRight(3); + assertThat(z.lefts(), is(Stream.single(2))); + assertThat(z.focus(), is(3)); + assertThat(z.rights(), is(Stream.nil())); + } + + @Test + public void testDeleteOthers() { + Zipper z = Zipper.zipper(Stream.single(1), 2, Stream.single(3)); + z = z.deleteOthers(); + assertThat(z.lefts(), is(Stream.nil())); + assertThat(z.focus(), is(2)); + assertThat(z.rights(), is(Stream.nil())); + } + + @Test + public void testFind() { + Zipper z = Zipper.zipper(Stream.nil(), 0, Stream.range(1)); + z = z.find(i -> i == 4).some(); + assertThat(z.lefts(), is(Stream.arrayStream(new Integer[]{3, 2, 1, 0}))); + assertThat(z.focus(), is(4)); + assertThat(z.rights().take(3), is(Stream.range(5, 8))); + } +} From 954a12b31c7430fe4af0aecb0492f02960cc0796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Thu, 2 Aug 2018 17:26:56 -0400 Subject: [PATCH 038/173] Add Vector tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/test/java/fj/data/vector/VTest.java | 62 ++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 core/src/test/java/fj/data/vector/VTest.java diff --git a/core/src/test/java/fj/data/vector/VTest.java b/core/src/test/java/fj/data/vector/VTest.java new file mode 100644 index 00000000..027b8407 --- /dev/null +++ b/core/src/test/java/fj/data/vector/VTest.java @@ -0,0 +1,62 @@ +package fj.data.vector; + +import fj.P; +import fj.P2; +import fj.P3; +import fj.P4; +import fj.P5; +import fj.P6; +import fj.P7; +import fj.P8; +import fj.data.Array; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class VTest { + @Test + public void testVectorUp(){ + final P2 p2 = P.p(2, 1); + final V2 v2 = V2.p(p2); + final V3 v3 = V3.cons(P.p(3), v2); + final V4 v4 = V4.cons(P.p(4), v3); + final V5 v5 = V5.cons(P.p(5), v4); + final V6 v6 = V6.cons(P.p(6), v5); + final V7 v7 = V7.cons(P.p(7), v6); + final V8 v8 = V8.cons(P.p(8), v7); + assertThat(v8.toArray(), is(Array.range(1, 9).reverse())); + } + + @Test + public void testVectorP(){ + final P2 p2 = P.p(1, 2); + final V2 v2 = V2.p(p2); + assertThat(v2.toArray(), is(Array.range(1, 3))); + assertThat(v2.p(), is(p2)); + final P3 p3 = p2.append(3); + final V3 v3 = V3.p(p3); + assertThat(v3.toArray(), is(Array.range(1, 4))); + assertThat(v3.p(), is(p3)); + final P4 p4 = p3.append(4); + final V4 v4 = V4.p(p4); + assertThat(v4.toArray(), is(Array.range(1, 5))); + assertThat(v4.p(), is(p4)); + final P5 p5 = p4.append(5); + final V5 v5 = V5.p(p5); + assertThat(v5.toArray(), is(Array.range(1, 6))); + assertThat(v5.p(), is(p5)); + final P6 p6 = p5.append(6); + final V6 v6 = V6.p(p6); + assertThat(v6.toArray(), is(Array.range(1, 7))); + assertThat(v6.p(), is(p6)); + final P7 p7 = p6.append(7); + final V7 v7 = V7.p(p7); + assertThat(v7.toArray(), is(Array.range(1, 8))); + assertThat(v7.p(), is(p7)); + final P8 p8 = p7.append(8); + final V8 v8 = V8.p(p8); + assertThat(v8.toArray(), is(Array.range(1, 9))); + assertThat(v8.p(), is(p8)); + } +} From ba27e71970287988d0ca5125d7316439dd7dd1ef Mon Sep 17 00:00:00 2001 From: Gabor Liptak Date: Thu, 2 Aug 2018 20:41:08 -0400 Subject: [PATCH 039/173] Add Optic tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/test/java/fj/data/optic/IsoTest.java | 43 ++++++ .../src/test/java/fj/data/optic/LensTest.java | 123 ++++++++++++++++++ .../test/java/fj/data/optic/OptionalTest.java | 29 +++++ .../test/java/fj/data/optic/PrismTest.java | 29 +++++ .../java/fj/data/optic/TraversalTest.java | 23 ++++ 5 files changed, 247 insertions(+) create mode 100644 core/src/test/java/fj/data/optic/IsoTest.java create mode 100644 core/src/test/java/fj/data/optic/LensTest.java create mode 100644 core/src/test/java/fj/data/optic/OptionalTest.java create mode 100644 core/src/test/java/fj/data/optic/PrismTest.java create mode 100644 core/src/test/java/fj/data/optic/TraversalTest.java diff --git a/core/src/test/java/fj/data/optic/IsoTest.java b/core/src/test/java/fj/data/optic/IsoTest.java new file mode 100644 index 00000000..6cace533 --- /dev/null +++ b/core/src/test/java/fj/data/optic/IsoTest.java @@ -0,0 +1,43 @@ +package fj.data.optic; + +import fj.P; +import fj.P2; +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class IsoTest { + @Test + public void testIso() { + final int oldNumber = 10; + final String oldStreet = "Main St"; + final Address oldAddress = new Address(oldNumber, oldStreet); + final Iso> addressIso = Iso.iso(p -> P.p(p.number, p.street), + p -> new Address(p._1(), p._2())); + final Address a = addressIso.reverseGet(addressIso.get(oldAddress)); + assertThat(a.number, is(oldAddress.number)); + assertThat(a.street, is(oldAddress.street)); + } + + static final class Person { + String name; + Address address; + + Person(String name, Address address) { + this.name = name; + this.address = address; + } + } + + static final class Address { + int number; + String street; + + public Address(int number, String street) { + this.number = number; + this.street = street; + } + } + +} diff --git a/core/src/test/java/fj/data/optic/LensTest.java b/core/src/test/java/fj/data/optic/LensTest.java new file mode 100644 index 00000000..d78fa0d0 --- /dev/null +++ b/core/src/test/java/fj/data/optic/LensTest.java @@ -0,0 +1,123 @@ +package fj.data.optic; + +import fj.F; +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class LensTest { + @Test + public void testLensPersonGet() { + final String oldName = "Joe"; + final int oldNumber = 10; + final String oldStreet = "Main St"; + final Address oldAddress = new Address(oldNumber, oldStreet); + final Person oldPerson = new Person(oldName, oldAddress); + final Lens personNameLens = Lens.lens(p -> p.name, s -> p -> new Person(s, p.address)); + final Lens personAddressLens = Lens.lens(p -> p.address, a -> p -> new Person(p.name, a)); + final Lens addressNumberLens = Lens.lens(a -> a.number, n -> a -> new Address(n, a.street)); + final Lens addressStreetLens = Lens.lens(a -> a.street, s -> a -> new Address(a.number, s)); + final Lens personNumberLens = personAddressLens.composeLens(addressNumberLens); + final Lens personStreetLens = personAddressLens.composeLens(addressStreetLens); + assertThat(personNameLens.get(oldPerson), is(oldName)); + assertThat(personNumberLens.get(oldPerson), is(oldNumber)); + assertThat(personStreetLens.get(oldPerson), is(oldStreet)); + } + + @Test + public void testLensPersonSetName() { + final String oldName = "Joe"; + final int oldNumber = 10; + final String oldStreet = "Main St"; + final Address oldAddress = new Address(oldNumber, oldStreet); + final Person oldPerson = new Person(oldName, oldAddress); + final Lens personNameLens = Lens.lens(p -> p.name, s -> p -> new Person(s, p.address)); + String newName = "Bill"; + Person p = personNameLens.set(newName).f(oldPerson); + assertThat(p.name, is(newName)); + assertThat(p.address, is(oldPerson.address)); + } + + @Test + public void testLensPersonSetNumber() { + final String oldName = "Joe"; + final int oldNumber = 10; + final String oldStreet = "Main St"; + final Address oldAddress = new Address(oldNumber, oldStreet); + final Person oldPerson = new Person(oldName, oldAddress); + final Lens personAddressLens = Lens.lens(p -> p.address, a -> p -> new Person(p.name, a)); + final Lens addressNumberLens = Lens.lens(a -> a.number, n -> a -> new Address(n, a.street)); + final Lens personNumberLens = personAddressLens.composeLens(addressNumberLens); + int newNumber = 20; + Person p = personNumberLens.set(newNumber).f(oldPerson); + assertThat(p.name, is(oldName)); + assertThat(p.address.number, is(newNumber)); + assertThat(p.address.street, is(oldStreet)); + } + + @Test + public void testLensPersonSetStreet() { + final String oldName = "Joe"; + final int oldNumber = 10; + final String oldStreet = "Main St"; + final Address oldAddress = new Address(oldNumber, oldStreet); + final Person oldPerson = new Person(oldName, oldAddress); + final Lens personAddressLens = Lens.lens(p -> p.address, a -> p -> new Person(p.name, a)); + final Lens addressNumberLens = Lens.lens(a -> a.number, n -> a -> new Address(n, a.street)); + final Lens addressStreetLens = Lens.lens(a -> a.street, s -> a -> new Address(a.number, s)); + final Lens personStreetLens = personAddressLens.composeLens(addressStreetLens); + String newStreet = "First St"; + Person p = personStreetLens.set(newStreet).f(oldPerson); + assertThat(p.name, is(oldName)); + assertThat(p.address.number, is(oldPerson.address.number)); + assertThat(p.address.street, is(newStreet)); + } + + @Test + public void testLensPersonSetter() { + final String oldName = "Joe"; + final int oldNumber = 10; + final String oldStreet = "Main St"; + final Address oldAddress = new Address(oldNumber, oldStreet); + final Person oldPerson = new Person(oldName, oldAddress); + final Lens personNameLens = Lens.lens(p -> p.name, s -> p -> new Person(s, p.address)); + String newName = "Bill"; + F setter = personNameLens.asSetter().set(newName); + Person p = setter.f(oldPerson); + assertThat(p.name, is(newName)); + assertThat(p.address, is(oldPerson.address)); + } + + @Test + public void testLensPersonGetter() { + final String oldName = "Joe"; + final int oldNumber = 10; + final String oldStreet = "Main St"; + final Address oldAddress = new Address(oldNumber, oldStreet); + final Person oldPerson = new Person(oldName, oldAddress); + final Lens personNameLens = Lens.lens(p -> p.name, s -> p -> new Person(s, p.address)); + assertThat(personNameLens.asGetter().get(oldPerson), is(oldName)); + } + + static final class Person { + String name; + Address address; + + Person(String name, Address address) { + this.name = name; + this.address = address; + } + } + + static final class Address { + int number; + String street; + + public Address(int number, String street) { + this.number = number; + this.street = street; + } + } + +} diff --git a/core/src/test/java/fj/data/optic/OptionalTest.java b/core/src/test/java/fj/data/optic/OptionalTest.java new file mode 100644 index 00000000..07c3f79a --- /dev/null +++ b/core/src/test/java/fj/data/optic/OptionalTest.java @@ -0,0 +1,29 @@ +package fj.data.optic; + +import fj.data.Option; +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class OptionalTest { + @Test + public void testOptionalSome() { + Optional o = Optional.optional(this::decode, i -> s -> s); + assertThat(o.getOption("18"), is(Option.some(18))); + } + + @Test + public void testOptionalNone() { + Optional o = Optional.optional(this::decode, i -> s -> s); + assertThat(o.getOption("Z"), is(Option.none())); + } + + private Option decode(String s) { + try { + return Option.some(Integer.decode(s)); + } catch (NumberFormatException nfe) { + return Option.none(); + } + } +} diff --git a/core/src/test/java/fj/data/optic/PrismTest.java b/core/src/test/java/fj/data/optic/PrismTest.java new file mode 100644 index 00000000..da128d72 --- /dev/null +++ b/core/src/test/java/fj/data/optic/PrismTest.java @@ -0,0 +1,29 @@ +package fj.data.optic; + +import fj.data.Option; +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class PrismTest { + @Test + public void testPrismSome() { + Prism prism = Prism.prism(s -> decode(s), i -> i.toString()); + assertThat(prism.getOption("18"), is(Option.some(18))); + } + + @Test + public void testPrismNone() { + Prism prism = Prism.prism(s -> decode(s), i -> i.toString()); + assertThat(prism.getOption("Z"), is(Option.none())); + } + + private Option decode(String s) { + try { + return Option.some(Integer.decode(s)); + } catch (NumberFormatException nfe) { + return Option.none(); + } + } +} diff --git a/core/src/test/java/fj/data/optic/TraversalTest.java b/core/src/test/java/fj/data/optic/TraversalTest.java new file mode 100644 index 00000000..cadd6ccf --- /dev/null +++ b/core/src/test/java/fj/data/optic/TraversalTest.java @@ -0,0 +1,23 @@ +package fj.data.optic; + +import fj.Monoid; +import fj.data.Either; +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class TraversalTest { + @Test + public void testTraversalLeft() { + final Traversal, Integer> t = Traversal.codiagonal(); + assertThat(t.fold(Monoid.intMinMonoid).f(Either.left(3)), is(3)); + } + + @Test + public void testTraversalRight() { + final Traversal, Integer> t = Traversal.codiagonal(); + assertThat(t.fold(Monoid.intMinMonoid).f(Either.right(2)), is(2)); + } + +} From 22cf4b4a06298c79701e61384fa5baffb0eb34a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Fri, 3 Aug 2018 21:03:45 -0400 Subject: [PATCH 040/173] Add Parser tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/main/java/fj/Equal.java | 8 +++ core/src/main/java/fj/Hash.java | 20 ++++++++ core/src/main/java/fj/Show.java | 14 ++++++ core/src/main/java/fj/parser/Result.java | 19 +++++++ core/src/test/java/fj/parser/ParserTest.java | 52 ++++++++++++++++++++ 5 files changed, 113 insertions(+) create mode 100644 core/src/test/java/fj/parser/ParserTest.java diff --git a/core/src/main/java/fj/Equal.java b/core/src/main/java/fj/Equal.java index 9cf2a72a..a798a736 100644 --- a/core/src/main/java/fj/Equal.java +++ b/core/src/main/java/fj/Equal.java @@ -10,6 +10,7 @@ import fj.data.vector.V6; import fj.data.vector.V7; import fj.data.vector.V8; +import fj.parser.Result; import java.math.BigDecimal; import java.math.BigInteger; @@ -355,6 +356,13 @@ public static Equal> eitherEqual(final Equal ea, final Eq )); } + public static Equal> resultEqual(final Equal ea, final Equal ei) { + Definition eaDef = ea.def; + Definition eiDef= ei.def; + return equalDef((r1, r2) -> eaDef.equal(r1.value(), r2.value()) && eiDef.equal(r1.rest(), r2.rest())); + } + + /** * An equal instance for the {@link Validation} type. * diff --git a/core/src/main/java/fj/Hash.java b/core/src/main/java/fj/Hash.java index 708fe853..da443ab2 100644 --- a/core/src/main/java/fj/Hash.java +++ b/core/src/main/java/fj/Hash.java @@ -10,6 +10,7 @@ import fj.data.vector.V6; import fj.data.vector.V7; import fj.data.vector.V8; +import fj.parser.Result; import java.math.BigDecimal; import java.math.BigInteger; @@ -162,6 +163,25 @@ public static Hash> eitherHash(final Hash ha, final Hash< return hash(e -> e.isLeft() ? ha.hash(e.left().value()) : hb.hash(e.right().value())); } + /** + * A hash instance for the {@link Result} type. + * + * @param ha Hash the Result value. + * @param hi Hash the Result remainder. + * @return A hash instance for the {@link Result} type. + */ + public static Hash> resultHash(Hash ha, Hash hi) { + return hash(res -> { + final int p = 419; + int r = 239; + + r = p * r + ha.hash(res.value()); + r = p * r + hi.hash(res.rest()); + + return r; + }); + } + /** * A hash instance for the {@link Validation} type. * diff --git a/core/src/main/java/fj/Show.java b/core/src/main/java/fj/Show.java index 2d2c4707..cd57897b 100644 --- a/core/src/main/java/fj/Show.java +++ b/core/src/main/java/fj/Show.java @@ -12,6 +12,7 @@ import fj.data.vector.V6; import fj.data.vector.V7; import fj.data.vector.V8; +import fj.parser.Result; import java.math.BigDecimal; import java.math.BigInteger; @@ -255,6 +256,19 @@ public static Show> eitherShow(final Show sa, final Show< fromString("Right(").append(sb.f.f(e.right().value())).append(single(')'))); } + /** + * A show instance for the {@link Result} type. + * + * @param sa Show for the {@link Result} value. + * @param si Show for the {@link Result} remainder. + * @return A show instance for the {@link Result} type. + */ + public static Show> resultShow(Show sa, Show si) { + return show(res -> + fromString("Result(").append(sa.f.f(res.value())) + .append(single(',')).append(si.f.f(res.rest())).append(single(')'))); + } + /** * A show instance for the {@link Validation} type. * diff --git a/core/src/main/java/fj/parser/Result.java b/core/src/main/java/fj/parser/Result.java index 51e115f6..b8acc6c4 100644 --- a/core/src/main/java/fj/parser/Result.java +++ b/core/src/main/java/fj/parser/Result.java @@ -1,6 +1,9 @@ package fj.parser; +import fj.Equal; import fj.F; +import fj.Hash; +import fj.Show; import static fj.Function.curry; @@ -21,6 +24,22 @@ private Result(final I i, final A a) { this.a = a; } + @Override + public final int hashCode() { + return Hash.resultHash(Hash.anyHash(), Hash.anyHash()).hash(this); + } + + @Override + public final String toString() { + return Show.resultShow(Show.anyShow(), Show.anyShow()).showS(this); + } + + @Override + public final boolean equals(Object other) { + return Equal.equals0(Result.class, this, other, () -> Equal.resultEqual(Equal.anyEqual(), Equal.anyEqual())); + } + + /** * The remainder of the parse input. * diff --git a/core/src/test/java/fj/parser/ParserTest.java b/core/src/test/java/fj/parser/ParserTest.java new file mode 100644 index 00000000..a3f623f5 --- /dev/null +++ b/core/src/test/java/fj/parser/ParserTest.java @@ -0,0 +1,52 @@ +package fj.parser; + +import fj.F; +import fj.F0; +import fj.data.Stream; +import fj.data.Validation; +import org.junit.Test; + +import static fj.parser.Result.result; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.*; + +public class ParserTest { + @Test + public void testParserFail() { + final Parser fail = Parser.fail(new ParseException()); + assertThat(fail.parse("").fail(), is(new ParseException())); + } + + @Test + public void testParserValue() { + final Parser p = Parser.parser(s -> s.isEmpty() ? + Validation.fail(new ParseException()) : + Validation.success(result(s.substring(1), s.substring(0, 1))) + ); + final Result r = p.parse("abc").success(); + assertThat(r.value(), is("a")); + assertThat(r.rest(), is("bc")); + } + + @Test + public void testParserBind() { + final Parser p = Parser.value("a"); + final Parser fail = Parser.fail(new ParseException()); + assertThat(p.bind(o -> fail).parse("aaaa").fail(), is(new ParseException())); + } + + @Test + public void testParserStream() { + Stream s = Stream.fromString("abc"); + Result, Character> r = Parser.CharsParser.character('a').parse(s).success(); + assertThat(r, is(Result.result(Stream.fromString("bc"), 'a'))); + } + + class ParseException extends Exception { + @Override + public boolean equals (Object obj) { + return (obj instanceof ParseException); + } + } + +} From ca67637f62aba38fc84056378601b9e88bb69a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sat, 4 Aug 2018 15:24:14 -0400 Subject: [PATCH 041/173] Implement Vector equals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/main/java/fj/data/vector/V2.java | 10 ++++ core/src/main/java/fj/data/vector/V3.java | 10 ++++ core/src/main/java/fj/data/vector/V4.java | 10 ++++ core/src/main/java/fj/data/vector/V5.java | 10 ++++ core/src/main/java/fj/data/vector/V6.java | 10 ++++ core/src/main/java/fj/data/vector/V7.java | 10 ++++ core/src/main/java/fj/data/vector/V8.java | 10 ++++ core/src/test/java/fj/data/vector/VTest.java | 60 +++++++++++++++++--- 8 files changed, 122 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/fj/data/vector/V2.java b/core/src/main/java/fj/data/vector/V2.java index fdab115c..d1ec5e74 100644 --- a/core/src/main/java/fj/data/vector/V2.java +++ b/core/src/main/java/fj/data/vector/V2.java @@ -23,6 +23,16 @@ private V2(final P2 inner) { this.inner = inner; } + @Override + public final boolean equals(Object other) { + return Equal.equals0(V2.class, this, other, () -> Equal.v2Equal(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.v2Hash(Hash.anyHash()).hash(this); + } + /** * Creates a vector-2 from a homogeneous product-2. * diff --git a/core/src/main/java/fj/data/vector/V3.java b/core/src/main/java/fj/data/vector/V3.java index 9b25258f..523c08ae 100644 --- a/core/src/main/java/fj/data/vector/V3.java +++ b/core/src/main/java/fj/data/vector/V3.java @@ -23,6 +23,16 @@ private V3(final P1 head, final V2 tail) { this.tail = tail; } + @Override + public final boolean equals(Object other) { + return Equal.equals0(V3.class, this, other, () -> Equal.v3Equal(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.v3Hash(Hash.anyHash()).hash(this); + } + /** * Creates a vector-3 from a homogeneous product-3. * diff --git a/core/src/main/java/fj/data/vector/V4.java b/core/src/main/java/fj/data/vector/V4.java index 1f39346b..808e7bab 100644 --- a/core/src/main/java/fj/data/vector/V4.java +++ b/core/src/main/java/fj/data/vector/V4.java @@ -23,6 +23,16 @@ private V4(final P1 head, final V3 tail) { this.tail = tail; } + @Override + public final boolean equals(Object other) { + return Equal.equals0(V4.class, this, other, () -> Equal.v4Equal(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.v4Hash(Hash.anyHash()).hash(this); + } + /** * Creates a vector-4 from a homogeneous product-4. * diff --git a/core/src/main/java/fj/data/vector/V5.java b/core/src/main/java/fj/data/vector/V5.java index 0119dcf9..8f0583a3 100644 --- a/core/src/main/java/fj/data/vector/V5.java +++ b/core/src/main/java/fj/data/vector/V5.java @@ -23,6 +23,16 @@ private V5(final P1 head, final V4 tail) { this.tail = tail; } + @Override + public final boolean equals(Object other) { + return Equal.equals0(V5.class, this, other, () -> Equal.v5Equal(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.v5Hash(Hash.anyHash()).hash(this); + } + /** * Creates a vector-5 from a homogeneous product-5. * diff --git a/core/src/main/java/fj/data/vector/V6.java b/core/src/main/java/fj/data/vector/V6.java index 47a0c4e7..880deaff 100644 --- a/core/src/main/java/fj/data/vector/V6.java +++ b/core/src/main/java/fj/data/vector/V6.java @@ -23,6 +23,16 @@ private V6(final P1 head, final V5 tail) { this.tail = tail; } + @Override + public final boolean equals(Object other) { + return Equal.equals0(V6.class, this, other, () -> Equal.v6Equal(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.v6Hash(Hash.anyHash()).hash(this); + } + /** * Creates a vector-6 from a homogeneous product-6. * diff --git a/core/src/main/java/fj/data/vector/V7.java b/core/src/main/java/fj/data/vector/V7.java index c0dfa4b6..2127d5f4 100644 --- a/core/src/main/java/fj/data/vector/V7.java +++ b/core/src/main/java/fj/data/vector/V7.java @@ -23,6 +23,16 @@ private V7(final P1 head, final V6 tail) { this.tail = tail; } + @Override + public final boolean equals(Object other) { + return Equal.equals0(V7.class, this, other, () -> Equal.v7Equal(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.v7Hash(Hash.anyHash()).hash(this); + } + /** * Creates a vector-7 from a homogeneous product-7. * diff --git a/core/src/main/java/fj/data/vector/V8.java b/core/src/main/java/fj/data/vector/V8.java index f01dc2fe..9ca7b618 100644 --- a/core/src/main/java/fj/data/vector/V8.java +++ b/core/src/main/java/fj/data/vector/V8.java @@ -23,6 +23,16 @@ private V8(final P1 head, final V7 tail) { this.tail = tail; } + @Override + public final boolean equals(Object other) { + return Equal.equals0(V8.class, this, other, () -> Equal.v8Equal(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.v8Hash(Hash.anyHash()).hash(this); + } + /** * Creates a vector-8 from a homogeneous product-8. * diff --git a/core/src/test/java/fj/data/vector/VTest.java b/core/src/test/java/fj/data/vector/VTest.java index 027b8407..9a3709ac 100644 --- a/core/src/test/java/fj/data/vector/VTest.java +++ b/core/src/test/java/fj/data/vector/VTest.java @@ -1,13 +1,6 @@ package fj.data.vector; -import fj.P; -import fj.P2; -import fj.P3; -import fj.P4; -import fj.P5; -import fj.P6; -import fj.P7; -import fj.P8; +import fj.*; import fj.data.Array; import org.junit.Test; @@ -59,4 +52,55 @@ public void testVectorP(){ assertThat(v8.toArray(), is(Array.range(1, 9))); assertThat(v8.p(), is(p8)); } + + @Test + public void testVectorFunc2() { + V2 v2 = V.v(() -> 2, () -> 1); + F2> fv2 = V.v2(); + V2 vf2 = fv2.f(2, 1); + assertThat(vf2, is(v2)); + } + + @Test + public void testVectorFunc3() { + V3 v3 = V.v(P.p(3), () -> 2, () -> 1); + F3> fv3 = V.v3(); + V3 vf3 = fv3.f(3, 2, 1); + assertThat(vf3, is(v3)); + } + + @Test + public void testVectorFunc4() { + V4 v4 = V.v(P.p(4), P.p(3), () -> 2, () -> 1); + F4> fv4 = V.v4(); + V4 vf4 = fv4.f(4, 3, 2, 1); + assertThat(vf4, is(v4)); + } + + @Test + public void testVectorFunc5() { + V5 v5 = V.v(P.p(5), P.p(4), P.p(3), () -> 2, () -> 1); + F5> fv5 = V.v5(); + V5 vf5 = fv5.f(5, 4, 3, 2, 1); + assertThat(vf5, is(v5)); + } + + @Test + public void testVectorMap() { + final V2 v2 = V.v(() -> 2, () -> 1); + assertThat(v2, is(v2.map(i -> i * 1))); + final V3 v3 = V3.cons(P.p(3), v2); + assertThat(v3, is(v3.map(i -> i * 1))); + final V4 v4 = V4.cons(P.p(4), v3); + assertThat(v4, is(v4.map(i -> i * 1))); + final V5 v5 = V5.cons(P.p(5), v4); + assertThat(v5, is(v5.map(i -> i * 1))); + final V6 v6 = V6.cons(P.p(6), v5); + assertThat(v6, is(v6.map(i -> i * 1))); + final V7 v7 = V7.cons(P.p(7), v6); + assertThat(v7, is(v7.map(i -> i * 1))); + final V8 v8 = V8.cons(P.p(8), v7); + assertThat(v8, is(v8.map(i -> i * 1))); + } + } From 7e149a63b478ce943b4e8c5d2cd5c7c909a71eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sat, 11 Aug 2018 19:42:46 -0400 Subject: [PATCH 042/173] Add FingerTree tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/test/java/fj/FWFunctionsTest.java | 1 - .../fj/data/fingertrees/FingerTreeTest.java | 37 ++++++++++++++----- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/core/src/test/java/fj/FWFunctionsTest.java b/core/src/test/java/fj/FWFunctionsTest.java index b9ce65d5..c1f69718 100644 --- a/core/src/test/java/fj/FWFunctionsTest.java +++ b/core/src/test/java/fj/FWFunctionsTest.java @@ -2,7 +2,6 @@ import org.junit.Test; -import fj.F1W; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; diff --git a/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java b/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java index 2a64b44b..57e1fae6 100644 --- a/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java +++ b/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java @@ -1,19 +1,19 @@ package fj.data.fingertrees; +import fj.Function; import fj.P; import fj.P2; -import fj.Show; import fj.data.List; -import fj.data.Stream; +import fj.data.Option; import org.junit.Test; -import static fj.P.p; -import static fj.Show.intShow; -import static fj.test.Property.prop; -import static fj.test.Property.property; -import static java.lang.System.out; +import static fj.Monoid.intAdditionMonoid; +import static fj.Monoid.intMinMonoid; +import static fj.data.fingertrees.FingerTree.measured; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; +import static org.hamcrest.core.Is.is; + /** * Created by MarkPerry on 10/10/2015. @@ -24,18 +24,35 @@ public class FingerTreeTest { @Test public void size() { - validateSize(List.list(-92, 68, 54, -77, -18, 67)); - validateSize(List.list(-92, 68, 54, -77, -18, 67, -60, 23, -70, 99, 66, -79, -5)); + validateOperations(List.list(-92, 68, 54, -77, -18, 67)); + validateOperations(List.list(-92, 68, 54, -77, -18, 67, -60, 23, -70, 99, 66, -79, -5)); } - void validateSize(List list) { + void validateOperations(List list) { FingerTree ft = list.foldLeft( (acc, i) -> acc.snoc(i), FingerTree.emptyIntAddition() ); assertThat(ft.measure(), equalTo(list.length())); + assertThat(ft.foldLeft((s, i) -> s + 1, 0), equalTo(list.length())); + assertThat(ft.foldRight((i, s) -> 1 + s, 0), equalTo(list.length())); + assertThat(ft.filter(e -> e.equals(-77)).head(), equalTo(-77)); assertThat(ft.length(), equalTo(list.length())); } + @Test + public void testHeadOption() { + assertThat(Empty.emptyIntAddition().headOption(), is(Option.none())); + FingerTree ft = new MakeTree(measured(intAdditionMonoid, Function.constant(1))).single(1); + assertThat(ft.headOption(), is(Option.some(1))); + } + + @Test + public void testUncons() { + assertThat(Empty.emptyIntAddition().uncons(0, (h, t) -> h), is(0)); + FingerTree ft = new MakeTree(measured(intAdditionMonoid, Function.constant(1))).single(1); + assertThat(ft.uncons(0, (h, t) -> h), is(1)); + } + public FingerTree midSeq() { FingerTree ft = FingerTree.emptyIntAddition(); return List.range(1, SIZE).foldLeft(ft2 -> i -> ft2.snoc(i), ft); From c2392a12e6e4a4f61508a2fd0f9e905bd5d65c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Mon, 13 Aug 2018 16:40:27 -0400 Subject: [PATCH 043/173] Add Reader/Writer tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- props-core/src/test/java/fj/data/ReaderTest.java | 16 +++++++++++++++- props-core/src/test/java/fj/data/WriterTest.java | 14 ++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/props-core/src/test/java/fj/data/ReaderTest.java b/props-core/src/test/java/fj/data/ReaderTest.java index 8494336e..87e2b52a 100644 --- a/props-core/src/test/java/fj/data/ReaderTest.java +++ b/props-core/src/test/java/fj/data/ReaderTest.java @@ -11,6 +11,8 @@ import static fj.test.Cogen.cogenInteger; import static fj.test.Property.prop; import static fj.test.Property.property; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** @@ -24,7 +26,6 @@ public void testMap() { // example taken from http://learnyouahaskell.com/for-a-few-monads-more int x = Reader.unit((Integer i) -> i + 3).map(i -> i * 5).f(8); assertTrue(x == 55); -// System.out.println(x); // 55 } @Test @@ -108,6 +109,19 @@ public void testAssociativity() { PropertyAssert.assertResult(p); } + @Test + public void testAndThen() { + final int y = Reader.unit((Integer i) -> i * 2).andThen(i -> i + 10).f(10); + assertThat(y, is(30)); + } + + @Test + public void testBind() { + final int y = Reader.unit((Integer i) -> i * 2) + .bind(a -> Reader.unit(i -> a + i + 11)).f(10); + assertThat(y, is(41)); + } + public Gen> arbReader() { return Arbitrary.arbReader(cogenInteger, arbInteger); } diff --git a/props-core/src/test/java/fj/data/WriterTest.java b/props-core/src/test/java/fj/data/WriterTest.java index 5f0ac122..25f99b22 100644 --- a/props-core/src/test/java/fj/data/WriterTest.java +++ b/props-core/src/test/java/fj/data/WriterTest.java @@ -2,8 +2,7 @@ import fj.Equal; import fj.F; -import fj.data.test.PropertyAssert; -import fj.test.Arbitrary; +import fj.P; import fj.test.Gen; import fj.test.Property; import org.junit.Assert; @@ -14,7 +13,8 @@ import static fj.test.Cogen.cogenInteger; import static fj.test.Property.prop; import static fj.test.Property.property; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; /** * Created by MarkPerry on 17/12/2014. @@ -107,7 +107,13 @@ public void testAssociativity() { assertResult(p); } - + @Test + public void testUnit() { + Writer w = Writer.unit("+").tell("foo").tell("bar"); + assertThat(w.run(), is(P.p("foobar", "+"))); + assertThat(w.log(), is("foobar")); + assertThat(w.value(), is("+")); + } } From 69fbd1d451d3382aee4e5bca5bd775b6631fc0d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sun, 12 Aug 2018 13:04:21 -0400 Subject: [PATCH 044/173] Add TreeZipper tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/main/java/fj/Equal.java | 20 ++++++++++++ core/src/main/java/fj/Hash.java | 26 ++++++++++++++- core/src/main/java/fj/data/TreeZipper.java | 25 +++++++++++++-- core/src/test/java/fj/FFunctionsTest.java | 18 +++++++++++ .../src/test/java/fj/data/TreeZipperTest.java | 32 +++++++++++++++++++ 5 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 core/src/test/java/fj/data/TreeZipperTest.java diff --git a/core/src/main/java/fj/Equal.java b/core/src/main/java/fj/Equal.java index a798a736..5cd31aeb 100644 --- a/core/src/main/java/fj/Equal.java +++ b/core/src/main/java/fj/Equal.java @@ -3,6 +3,7 @@ import fj.data.*; import fj.data.hamt.BitSet; import fj.data.hlist.HList; +import fj.data.optic.Traversal; import fj.data.vector.V2; import fj.data.vector.V3; import fj.data.vector.V4; @@ -466,6 +467,25 @@ public static Equal> zipperEqual(final Equal ea) { } /** + * An equal instance for the {@link TreeZipper} type. + * + * @param ea Equality across the elements of the tree zipper. + * @return An equal instance for the {@link TreeZipper} type. + */ + public static Equal> treeZipperEqual(final Equal ea) { + final Equal> te = Equal.treeEqual(ea); + final Equal>> st = streamEqual(Equal.treeEqual(ea)); + final Equal>, A, Stream>>>> sp = + streamEqual(p3Equal(streamEqual(treeEqual(ea)), ea, streamEqual(treeEqual(ea)))); + return equalDef((a1, a2) -> + te.eq(a1.focus(), a2.focus()) && + st.eq(a1.lefts(), a2.lefts()) && + st.eq(a1.rights(), a2.rights()) && + sp.eq(a1.parents(), a2.parents()) + ); + } + + /** * An equal instance for the {@link Array} type. * * @param ea Equality across the elements of the array. diff --git a/core/src/main/java/fj/Hash.java b/core/src/main/java/fj/Hash.java index da443ab2..8d0a4e4c 100644 --- a/core/src/main/java/fj/Hash.java +++ b/core/src/main/java/fj/Hash.java @@ -302,7 +302,31 @@ public static Hash> zipperHash(final Hash ha) { }); } - /** + /** + * A hash instance for the {@link TreeZipper} type. + * + * @param ha A hash for the elements of the tree zipper. + * @return A hash instance for the {@link TreeZipper} type. + */ + public static Hash> treeZipperHash(final Hash ha) { + Hash> th = treeHash(ha); + Hash>> sth = streamHash(treeHash(ha)); + Hash>, A, Stream>>>> tsp = + streamHash(p3Hash(streamHash(treeHash(ha)), ha, streamHash(treeHash(ha)))); + return hash(as -> { + final int p = 419; + int r = 239; + + r = p * r + th.hash(as.focus()); + r = p * r + sth.hash(as.lefts()); + r = p * r + sth.hash(as.rights()); + r = p * r + tsp.hash(as.parents()); + + return r; + }); + } + + /** * A hash instance for the {@link Tree} type. * * @param ha A hash for the elements of the tree. diff --git a/core/src/main/java/fj/data/TreeZipper.java b/core/src/main/java/fj/data/TreeZipper.java index c94287cf..46392d20 100644 --- a/core/src/main/java/fj/data/TreeZipper.java +++ b/core/src/main/java/fj/data/TreeZipper.java @@ -54,7 +54,17 @@ private TreeZipper(final Tree tree, this.parents = parents; } - /** + @Override + public final boolean equals(Object other) { + return Equal.equals0(TreeZipper.class, this, other, () -> Equal.treeZipperEqual(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.treeZipperHash(Hash.anyHash()).hash(this); + } + + /** * Creates a new tree zipper given a currently selected tree, a forest on the left, a forest on the right, * and a stream of parent contexts. * @@ -341,8 +351,17 @@ public Stream> lefts() { * @return the right siblings of the currently focused node. */ public Stream> rights() { - return rights; - } + return rights; + } + + /** + * Returns the parents of the currently focused node. + * + * @return the parents of the currently focused node. + */ + public Stream>, A, Stream>>> parents() { + return parents; + } /** * Indicates whether the current node is at the top of the tree. diff --git a/core/src/test/java/fj/FFunctionsTest.java b/core/src/test/java/fj/FFunctionsTest.java index 9d38e1f5..e13b1f2b 100644 --- a/core/src/test/java/fj/FFunctionsTest.java +++ b/core/src/test/java/fj/FFunctionsTest.java @@ -1,5 +1,7 @@ package fj; +import fj.data.Tree; +import fj.data.TreeZipper; import org.junit.Test; import static org.hamcrest.core.Is.is; import static org.junit.Assert.*; @@ -22,4 +24,20 @@ public void testApply() { F f1 = F2Functions.f(f2, 2); assertThat(F1Functions.f(f1, 1).f(), is(36)); } + + @Test + public void testTreeK() { + final Tree t1 = F1Functions.treeK(Function.identity()).f(1); + final Tree t2 = F1Functions.treeK(Function.identity()).f(2); + assertThat(F1Functions.mapTree(i -> i + 1).f(t1), + is(F1Functions.mapTree(i -> i * 1).f(t2))); + } + + @Test + public void testTreeZipperK() { + final TreeZipper tz1 = F1Functions.treeZipperK(Function.identity()).f(1); + final TreeZipper tz2 = F1Functions.treeZipperK(Function.identity()).f(2); + assertThat(F1Functions.mapTreeZipper(i -> i + 1).f(tz1), + is(F1Functions.mapTreeZipper(i -> i * 1).f(tz2))); + } } diff --git a/core/src/test/java/fj/data/TreeZipperTest.java b/core/src/test/java/fj/data/TreeZipperTest.java new file mode 100644 index 00000000..c52f4111 --- /dev/null +++ b/core/src/test/java/fj/data/TreeZipperTest.java @@ -0,0 +1,32 @@ +package fj.data; + +import org.junit.Test; + +import static fj.data.Option.none; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class TreeZipperTest { + @Test + public void testDelete() { + final Tree t = Tree.node(1, List.single(Tree.leaf(2))); + final TreeZipper tz = TreeZipper.fromTree(t); + assertThat(tz.delete(), is(none())); + } + + @Test + public void testDeleteForest() { + final Tree t = Tree.node(1, List.single(Tree.leaf(2))); + final TreeZipper tz = TreeZipper.fromForest(Stream.single(t)).some(); + assertThat(tz.delete(), is(none())); + + } + + @Test + public void testHash() { + final Tree t = Tree.node(1, List.single(Tree.leaf(2))); + final TreeZipper tz1 = TreeZipper.fromForest(Stream.single(t)).some(); + final TreeZipper tz2 = TreeZipper.fromForest(Stream.single(t)).some(); + assertThat(tz1.hashCode(), is(tz2.hashCode())); + } +} From 02d05ebaa451c33a1c0eedbcc1408e35eec7ad60 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sat, 18 Aug 2018 22:41:08 +0200 Subject: [PATCH 045/173] ChangeLog of 4.8 release. --- .changelogged.yaml | 14 ++++++++++++++ ChangeLog.md | 33 +++++++++++++++++++++++++++++++++ build.gradle | 4 ++-- 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 .changelogged.yaml create mode 100644 ChangeLog.md diff --git a/.changelogged.yaml b/.changelogged.yaml new file mode 100644 index 00000000..46180488 --- /dev/null +++ b/.changelogged.yaml @@ -0,0 +1,14 @@ +changelogs: + - changelog: ChangeLog.md + + ignore_files: + - "*ChangeLog*.md" + - .changelogged.yaml + + ignore_commits: [] + +branch: upstream/master + +entry_format: " - %message% (see [%link%](%id%));" + +editor_command: "nano -EiT 2" diff --git a/ChangeLog.md b/ChangeLog.md new file mode 100644 index 00000000..24276f35 --- /dev/null +++ b/ChangeLog.md @@ -0,0 +1,33 @@ +4.8 +--- + +### Enhancements + +- Enable upload of snapshot artifacts. (see [`e834e8b`](https://github.com/functionaljava/functionaljava/commit/e834e8b)); +- Add append methods to all Px classes. Fix #326 (see [`065ed43`](https://github.com/functionaljava/functionaljava/commit/065ed43)); +- Introduce the Eval monad (see [`98294fc`](https://github.com/functionaljava/functionaljava/commit/98294fc)); +- Fluent Equal/Ord construction (see [#333](https://github.com/functionaljava/functionaljava/pull/333)); +- Implement Zipper Eq and Hash and add tests (see [#343](https://github.com/functionaljava/functionaljava/pull/343)); +- Implement Vector equals (see [#350](https://github.com/functionaljava/functionaljava/pull/350)); + +### Fixes + +- Fixed a bug in the NonEmptyList Semigroup implementation that resulted in the same NonEmptyList appended to itself. (Regression in 4.7, see [`07f94fa`](https://github.com/functionaljava/functionaljava/commit/07f94fa)); +- Fixes #334: exception in Either.LeftProjection.traverseIO (see [#335](https://github.com/functionaljava/functionaljava/pull/335)); + +### Internal + +- Added Scalacheck Arbitrary implementations for Natural and NonEmptyList. (see [`405c3ec`](https://github.com/functionaljava/functionaljava/commit/405c3ec)); +- Added unit test coverage for Semigroup implementations. The StringBuffer and StringBuilder tests fail because both of those types are mutable. The IO test fails because the ArbitraryIO implementation does not implement equals. (see [`ef81130`](https://github.com/functionaljava/functionaljava/commit/ef81130)); +- Fixed the ArbitraryIO implementation and created a Properties object for testing the IO semigroup. (see [`a8e979f`](https://github.com/functionaljava/functionaljava/commit/a8e979f)); +- Added working tests coverage for the StringBuffer and StringBuilder semigroup implementations. Added the Semigroup tests to the list of Scalacheck test suite. (see [`aa4de33`](https://github.com/functionaljava/functionaljava/commit/aa4de33)); +- Equal: remove reference to static field of LazyString. Fix #321 (see [`6c6dabd`](https://github.com/functionaljava/functionaljava/commit/6c6dabd)); +- Add IOFunctions tests (see [#340](https://github.com/functionaljava/functionaljava/pull/340)); +- Add Stream tests (see [#341](https://github.com/functionaljava/functionaljava/pull/341)); +- Add tests for Try, F, FW, Digit (see [#346](https://github.com/functionaljava/functionaljava/pull/346)); +- Add Vector tests (see [#347](https://github.com/functionaljava/functionaljava/pull/347)); +- Add Optic tests (see [#348](https://github.com/functionaljava/functionaljava/pull/348)); +- Add Parser tests (see [#349](https://github.com/functionaljava/functionaljava/pull/349)); +- Add FingerTree tests (see [#351](https://github.com/functionaljava/functionaljava/pull/351)); +- Add TreeZipper tests (see [#352](https://github.com/functionaljava/functionaljava/pull/352)); +- Add Reader/Writer tests (see [#353](https://github.com/functionaljava/functionaljava/pull/353)); \ No newline at end of file diff --git a/build.gradle b/build.gradle index a1098cbb..9410f205 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ allprojects { defaultTasks "build" ext { - isSnapshot = true + isSnapshot = false fjBaseVersion = "4.8" snapshotAppendix = "-SNAPSHOT" @@ -53,7 +53,7 @@ allprojects { fjConsumeVersion = "4.7" signModule = false - useRetroLambda = false + useRetroLambda = true projectTitle = "Functional Java" projectName = "functionaljava" From 8cce3ee2d3c221a67b06e48f616532ecfa58b122 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sat, 18 Aug 2018 23:29:19 +0200 Subject: [PATCH 046/173] Update readme. Prepare next iteration. --- README.adoc | 18 +++++++++--------- build.gradle | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.adoc b/README.adoc index 6c5d3c1e..a7e2d3a4 100644 --- a/README.adoc +++ b/README.adoc @@ -35,12 +35,12 @@ The Functional Java artifact is published to Maven Central using the group `org. * Java 8 specific support (`functionaljava-java8`) * property based testing (`functionaljava-quickcheck` or `functionaljava-quickcheck_1.8` if you use Java 8+) -The latest stable version is `4.7`. This can be added to your Gradle project by adding the dependencies: +The latest stable version is `4.8`. This can be added to your Gradle project by adding the dependencies: ---- -compile "org.functionaljava:functionaljava:4.7" -compile "org.functionaljava:functionaljava-java8:4.7" -compile "org.functionaljava:functionaljava-quickcheck:4.7" -compile "org.functionaljava:functionaljava-java-core:4.7" +compile "org.functionaljava:functionaljava:4.8" +compile "org.functionaljava:functionaljava-java8:4.8" +compile "org.functionaljava:functionaljava-quickcheck:4.8" +compile "org.functionaljava:functionaljava-java-core:4.8" ---- and in Maven: @@ -48,22 +48,22 @@ and in Maven: org.functionaljava functionaljava - 4.7 + 4.8 org.functionaljava functionaljava-java8 - 4.7 + 4.8 org.functionaljava functionaljava-quickcheck - 4.7 + 4.8 org.functionaljava functionaljava-java-core - 4.7 + 4.8 ---- diff --git a/build.gradle b/build.gradle index 9410f205..fe81f484 100644 --- a/build.gradle +++ b/build.gradle @@ -44,16 +44,16 @@ allprojects { defaultTasks "build" - ext { - isSnapshot = false - fjBaseVersion = "4.8" + ext { + isSnapshot = true + fjBaseVersion = "4.9" snapshotAppendix = "-SNAPSHOT" fjVersion = fjBaseVersion + (isSnapshot ? snapshotAppendix : "") - fjConsumeVersion = "4.7" + fjConsumeVersion = "4.8" signModule = false - useRetroLambda = true + useRetroLambda = false projectTitle = "Functional Java" projectName = "functionaljava" From fd9b337abe0927bc8d2f5b149198bc84a77c0b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sat, 18 Aug 2018 18:23:40 -0400 Subject: [PATCH 047/173] Add Visitor tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- .../test/java/fj/function/VisitorTest.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 core/src/test/java/fj/function/VisitorTest.java diff --git a/core/src/test/java/fj/function/VisitorTest.java b/core/src/test/java/fj/function/VisitorTest.java new file mode 100644 index 00000000..54efcf24 --- /dev/null +++ b/core/src/test/java/fj/function/VisitorTest.java @@ -0,0 +1,80 @@ +package fj.function; + +import fj.Equal; +import fj.F; +import fj.P; +import fj.P1; +import fj.data.List; +import org.junit.Test; + +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.function.Visitor.*; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class VisitorTest { + @Test + public void testFindFirst() { + assertThat(findFirst(List.list(none(), some(1), none()), () -> -1), is(1)); + } + + @Test + public void testFindFirstDef() { + assertThat(findFirst(List.list(none(), none(), none()), () -> -1), is(-1)); + } + + @Test + public void testNullableFindFirst() { + assertThat(nullablefindFirst(List.list(null, 1, null), () -> -1), is(1)); + } + + @Test + public void testNullableFindFirstDef() { + assertThat(nullablefindFirst(List.list(null, null, null), () -> -1), is(-1)); + } + + @Test + public void testVisitor() { + assertThat(visitor(List.list(i -> some(2 * i)), () -> -1, 10), is(20)); + } + + @Test + public void testVisitorDef() { + assertThat(visitor(List.list(i -> none()), () -> "foo", 10), is("foo")); + } + + @Test + public void testNullableVisitor() { + assertThat(nullableVisitor(List.list(i -> 2 * i), () -> -1, 10), is(20)); + } + + @Test + public void testNullableVisitorDef() { + assertThat(nullableVisitor(List.list(i -> null), () -> "foo", 10), is("foo")); + } + + @Test + public void testAssociation() { + final F> a = association(List.list(P.p(1, "one"), P.p(2, "two")), Equal.intEqual); + assertThat(a.f("foo").f(2), is("two")); + } + + @Test + public void testAssociationDef() { + final F> a = association(List.list(P.p(1, "one"), P.p(2, "two")), Equal.intEqual); + assertThat(a.f("foo").f(3), is("foo")); + } + + @Test + public void testAssociationLazy() { + final F, F> a = associationLazy(List.list(P.p(1, "one"), P.p(2, "two")), Equal.intEqual); + assertThat(a.f(P.p("foo")).f(2), is("two")); + } + + @Test + public void testAssociationLazyDef() { + final F, F> a = associationLazy(List.list(P.p(1, "one"), P.p(2, "two")), Equal.intEqual); + assertThat(a.f(P.p("foo")).f(3), is("foo")); + } +} From f1801d3c4fa3b8c8bdfddd185b7e20bb4ce65860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Mon, 20 Aug 2018 20:01:15 -0400 Subject: [PATCH 048/173] Add DList tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/test/java/fj/data/DListTest.java | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 core/src/test/java/fj/data/DListTest.java diff --git a/core/src/test/java/fj/data/DListTest.java b/core/src/test/java/fj/data/DListTest.java new file mode 100644 index 00000000..6846ca2d --- /dev/null +++ b/core/src/test/java/fj/data/DListTest.java @@ -0,0 +1,31 @@ +package fj.data; + +import org.junit.Test; + +import static fj.data.DList.*; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class DListTest { + @Test + public void testConsSnoc() { + assertThat(nil().snoc(2).cons(1).toJavaList(), is(single(1).snoc(2).toJavaList())); + } + + @Test + public void testListDList() { + DList d = listDList(List.range(0, 1000)); + assertThat(d.toJavaList(), is(List.range(0, 1000).toJavaList())); + } + + @Test + public void testArrayDList() { + DList d = arrayDList(Array.range(0, 1000).array(Integer[].class)); + assertThat(d.toJavaList(), is(Array.range(0, 1000).toJavaList())); + } + @Test + public void testIter() { + DList d = iteratorDList(List.range(0, 1000).iterator()); + assertThat(d.toJavaList(), is(List.range(0, 1000).toJavaList())); + } +} From 58e0b4b556b870507dc6d7b788101b60f328f8b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Thu, 23 Aug 2018 10:05:41 -0400 Subject: [PATCH 049/173] Exclude consume/ from coverage --- codecov.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codecov.yml b/codecov.yml index 8c7fd5e4..b7ddeec5 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,3 +1,4 @@ ignore: - "demo/.*" - - "performance/.*" \ No newline at end of file + - "consume/.*" + - "performance/.*" From 33d5e8338f9c6a90a44882e61e821632be996990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Mon, 27 Aug 2018 21:25:41 -0400 Subject: [PATCH 050/173] Add P tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/test/java/fj/PTest.java | 159 +++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 core/src/test/java/fj/PTest.java diff --git a/core/src/test/java/fj/PTest.java b/core/src/test/java/fj/PTest.java new file mode 100644 index 00000000..2a97303f --- /dev/null +++ b/core/src/test/java/fj/PTest.java @@ -0,0 +1,159 @@ +package fj; + +import org.junit.Test; + +import static fj.Function.identity; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class PTest { + @Test + public void testPF(){ + final F> p1f = P.p1(); + final P1 p1 = p1f.f(1); + F>> p2f = P.p2(); + final P2 p2 = p2f.f(1).f(2); + assertThat(P2.__1().f(p2), is(P1.__1().f(p1))); + final F>>> p3f = P.p3(); + final P3 p3 = p3f.f(1).f(2).f(3); + assertThat(P3.__1().f(p3), is(P2.__1().f(p2))); + assertThat(P3.__2().f(p3), is(P2.__2().f(p2))); + final F>>>> p4f = P.p4(); + final P4 p4 = p4f.f(1).f(2).f(3).f(4); + assertThat(P4.__1().f(p4), is(P3.__1().f(p3))); + assertThat(P4.__2().f(p4), is(P3.__2().f(p3))); + assertThat(P4.__3().f(p4), is(P3.__3().f(p3))); + final F>>>>> p5f = P.p5(); + final P5 p5 = p5f.f(1).f(2).f(3).f(4).f(5); + assertThat(P5.__1().f(p5), is(P4.__1().f(p4))); + assertThat(P5.__2().f(p5), is(P4.__2().f(p4))); + assertThat(P5.__3().f(p5), is(P4.__3().f(p4))); + assertThat(P5.__4().f(p5), is(P4.__4().f(p4))); + final F>>>>>> p6f = P.p6(); + final P6 p6 = p6f.f(1).f(2).f(3).f(4).f(5).f(6); + assertThat(P6.__1().f(p6), + is(P5.__1().f(p5))); + assertThat(P6.__2().f(p6), + is(P5.__2().f(p5))); + assertThat(P6.__3().f(p6), + is(P5.__3().f(p5))); + assertThat(P6.__4().f(p6), + is(P5.__4().f(p5))); + assertThat(P6.__5().f(p6), + is(P5.__5().f(p5))); + final F>>>>>>> p7f = P.p7(); + final P7 p7 = + p7f.f(1).f(2).f(3).f(4).f(5).f(6).f(7); + assertThat(P7.__1().f(p7), + is(P6.__1().f(p6))); + assertThat(P7.__2().f(p7), + is(P6.__2().f(p6))); + assertThat(P7.__3().f(p7), + is(P6.__3().f(p6))); + assertThat(P7.__4().f(p7), + is(P6.__4().f(p6))); + assertThat(P7.__5().f(p7), + is(P6.__5().f(p6))); + assertThat(P7.__6().f(p7), + is(P6.__6().f(p6))); + final F>>>>>>>> p8f = P.p8(); + final P8 p8 = + p8f.f(1).f(2).f(3).f(4).f(5).f(6).f(7).f(8); + assertThat(P8.__1().f(p8), + is(P7.__1().f(p7))); + assertThat(P8.__2().f(p8), + is(P7.__2().f(p7))); + assertThat(P8.__3().f(p8), + is(P7.__3().f(p7))); + assertThat(P8.__4().f(p8), + is(P7.__4().f(p7))); + assertThat(P8.__5().f(p8), + is(P7.__5().f(p7))); + assertThat(P8.__6().f(p8), + is(P7.__6().f(p7))); + assertThat(P8.__7().f(p8), + is(P7.__7().f(p7))); + assertThat(P8.__8().f(p8), is(8)); + } + + @Test + public void testPProject1() { + final P1 p1 = P.p(1); + assertThat(p1.map(identity()), is(p1)); + } + + @Test + public void testPProject2() { + final P2 p2 = P.p(1, 2); + assertThat(p2.map1(identity()), is(p2)); + assertThat(p2.map2(identity()), is(p2)); + } + + @Test + public void testPProject3() { + final P3 p3 = P.p(1, 2, 3); + assertThat(p3.map1(identity()), is(p3)); + assertThat(p3.map2(identity()), is(p3)); + assertThat(p3.map3(identity()), is(p3)); + } + + @Test + public void testPProject4() { + final P4 p4 = P.p(1, 2, 3, 4); + assertThat(p4.map1(identity()), is(p4)); + assertThat(p4.map2(identity()), is(p4)); + assertThat(p4.map3(identity()), is(p4)); + assertThat(p4.map4(identity()), is(p4)); + } + + @Test + public void testPProject5() { + final P5 p5 = P.p(1, 2, 3, 4, 5); + assertThat(p5.map1(identity()), is(p5)); + assertThat(p5.map2(identity()), is(p5)); + assertThat(p5.map3(identity()), is(p5)); + assertThat(p5.map4(identity()), is(p5)); + assertThat(p5.map5(identity()), is(p5)); + } + + @Test + public void testPProject6() { + final P6 p6 = P.p(1, 2, 3, 4, 5, 6); + assertThat(p6.map1(identity()), is(p6)); + assertThat(p6.map2(identity()), is(p6)); + assertThat(p6.map3(identity()), is(p6)); + assertThat(p6.map4(identity()), is(p6)); + assertThat(p6.map5(identity()), is(p6)); + assertThat(p6.map6(identity()), is(p6)); + } + + @Test + public void testPProject7() { + final P7 p7 = + P.p(1, 2, 3, 4, 5, 6, 7); + assertThat(p7.map1(identity()), is(p7)); + assertThat(p7.map2(identity()), is(p7)); + assertThat(p7.map3(identity()), is(p7)); + assertThat(p7.map4(identity()), is(p7)); + assertThat(p7.map5(identity()), is(p7)); + assertThat(p7.map6(identity()), is(p7)); + assertThat(p7.map7(identity()), is(p7)); + } + + @Test + public void testPProject8() { + final P8 p8 = + P.p(1, 2, 3, 4, 5, 6, 7, 8); + assertThat(p8.map1(identity()), is(p8)); + assertThat(p8.map2(identity()), is(p8)); + assertThat(p8.map3(identity()), is(p8)); + assertThat(p8.map4(identity()), is(p8)); + assertThat(p8.map5(identity()), is(p8)); + assertThat(p8.map6(identity()), is(p8)); + assertThat(p8.map7(identity()), is(p8)); + assertThat(p8.map8(identity()), is(p8)); + } +} From ebdceaa7653780d8c896ad08687a22b934538aa5 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sat, 25 Aug 2018 21:16:44 +0200 Subject: [PATCH 051/173] Fix compile under jdk11. Enable jdk11 travis build. --- .travis.yml | 21 +- build.gradle | 13 +- core/src/main/java/fj/data/$.java | 6 - core/src/main/java/fj/data/IOFunctions.java | 234 +++++++++--------- core/src/main/java/fj/data/Java.java | 22 +- core/src/main/java/fj/data/TreeZipper.java | 17 +- demo/build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 3 +- java-core/build.gradle | 2 +- props-core-scalacheck/build.gradle | 4 +- .../src/main/java/fj/test/Arbitrary.java | 174 +++++-------- quickcheck/src/main/java/fj/test/Arg.java | 9 +- .../src/main/java/fj/test/CheckResult.java | 18 +- quickcheck/src/main/java/fj/test/Cogen.java | 7 +- quickcheck/src/main/java/fj/test/Gen.java | 2 +- .../src/main/java/fj/test/Property.java | 30 +-- quickcheck/src/main/java/fj/test/Shrink.java | 5 +- .../src/main/java/fj/test/reflect/Check.java | 8 +- .../java/fj/test/reflect/CheckParams.java | 1 + .../src/main/java/fj/test/reflect/Main.java | 3 - .../fj/test/runner/PropertyTestRunner.java | 9 +- .../src/test/java/fj/data/test/TestCheck.java | 12 +- .../src/test/java/fj/data/test/TestNull.java | 10 +- quickcheck/src/test/java/fj/test/GenTest.java | 18 +- 24 files changed, 247 insertions(+), 383 deletions(-) diff --git a/.travis.yml b/.travis.yml index 539e7a45..7c55b120 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,17 +4,22 @@ sudo: false language: java jdk: - - oraclejdk8 + - openjdk11 + +matrix: + + include: + - jdk: openjdk8 + script: + - ./gradlew build coverage -s -i + after_success: + - bash <(curl -s https://codecov.io/bash) + - '[ "$TRAVIS_BRANCH" = "series/4.x" -a "$TRAVIS_PULL_REQUEST" = "false" -a -z "$TRAVIS_TAG" ] + && ./gradlew uploadArchives' script: - - jdk_switcher use openjdk7 && export JAVA7_HOME=$JAVA_HOME - - jdk_switcher use oraclejdk8 && export JAVA8_HOME=$JAVA_HOME - - ./gradlew build coverage -s -i + - ./gradlew clean test -after_success: - - bash <(curl -s https://codecov.io/bash) - - '[ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" -a -z "$TRAVIS_TAG" ] - && ./gradlew uploadArchives' env: global: diff --git a/build.gradle b/build.gradle index fe81f484..9d98d2b3 100644 --- a/build.gradle +++ b/build.gradle @@ -23,8 +23,6 @@ buildscript { } } -apply plugin: "jacoco" - if (JavaVersion.current().isJava8Compatible()) { allprojects { tasks.withType(Javadoc) { @@ -35,11 +33,10 @@ if (JavaVersion.current().isJava8Compatible()) { allprojects { - + apply plugin: "jacoco" jacoco { - toolVersion = "0.8.1" - + toolVersion = "0.8.2" } defaultTasks "build" @@ -100,8 +97,6 @@ subprojects { } } - apply plugin: "jacoco" - apply from: "$rootDir/lib.gradle" apply plugin: "java" apply plugin: "eclipse" @@ -123,9 +118,13 @@ subprojects { } jacocoTestReport { + additionalSourceDirs = files(sourceSets.main.allSource.srcDirs) + sourceDirectories = files(sourceSets.main.allSource.srcDirs) + classDirectories = files(sourceSets.main.output) reports { html.enabled = true xml.enabled = true + csv.enabled = false } } diff --git a/core/src/main/java/fj/data/$.java b/core/src/main/java/fj/data/$.java index 17b2ac9e..a1aa77bf 100644 --- a/core/src/main/java/fj/data/$.java +++ b/core/src/main/java/fj/data/$.java @@ -17,14 +17,8 @@ public final class $ extends P1 { /** * Returns a function that given an argument, returns a function that ignores its argument. - * @deprecated JDK 8 warns '_' may not be supported after SE 8. As of release 4.4, use {@link #constant} (note the synonym {@link #__}). * @return A function that given an argument, returns a function that ignores its argument. */ - @Deprecated - public static $ _(final B b) { - return constant(b); - } - public static $ __(final B b) { return constant(b); } diff --git a/core/src/main/java/fj/data/IOFunctions.java b/core/src/main/java/fj/data/IOFunctions.java index 92263d09..2ea15dfb 100644 --- a/core/src/main/java/fj/data/IOFunctions.java +++ b/core/src/main/java/fj/data/IOFunctions.java @@ -1,8 +1,19 @@ package fj.data; -import static fj.Bottom.errorF; -import static fj.Function.constant; -import static fj.Function.partialApply2; +import fj.F; +import fj.F0; +import fj.F1Functions; +import fj.F2; +import fj.Function; +import fj.P; +import fj.P1; +import fj.P2; +import fj.Try; +import fj.Unit; +import fj.data.Iteratee.Input; +import fj.data.Iteratee.IterV; +import fj.function.Try0; +import fj.function.Try1; import java.io.BufferedReader; import java.io.Closeable; @@ -14,11 +25,9 @@ import java.nio.charset.Charset; import java.util.Arrays; -import fj.*; -import fj.data.Iteratee.Input; -import fj.data.Iteratee.IterV; -import fj.function.Try0; -import fj.function.Try1; +import static fj.Bottom.errorF; +import static fj.Function.constant; +import static fj.Function.partialApply2; /** * IO monad for processing files, with main methods {@link #enumFileLines }, @@ -170,38 +179,37 @@ public static SafeIO lazySafe(final F0 f) { * A function that feeds an iteratee with lines read from a {@link BufferedReader}. */ public static F, IO>>> lineReader() { - final F, Boolean> isDone = - new F, Boolean>() { - final F>, P1> done = constant(P.p(true)); - final F, IterV>, P1> cont = constant(P.p(false)); - - @Override - public Boolean f(final IterV i) { - return i.fold(done, cont)._1(); - } - }; - - return r -> new F, IO>>() { - final F>, P1>> done = errorF("iteratee is done"); //$NON-NLS-1$ - - @Override - public IO> f(final IterV it) { - // use loop instead of recursion because of missing TCO - return () -> { - IterV i = it; - while (!isDone.f(i)) { - final String s = r.readLine(); - if (s == null) { - return i; - } - final Input input = Input.el(s); - final F, IterV>, P1>> cont = F1Functions.lazy(Function.apply(input)); - i = i.fold(done, cont)._1(); + return LineReader::new; + } + + private static class LineReader implements F, IO>> { + + private final BufferedReader r; + + private final F, Boolean> isDone = i -> i.fold(constant(P.p(true)), constant(P.p(false)))._1(); + private final F>, P1>> done = errorF("iteratee is done"); //$NON-NLS-1$ + + private LineReader(BufferedReader r) { + this.r = r; + } + + @Override + public IO> f(IterV it) { + // use loop instead of recursion because of missing TCO + return () -> { + IterV i = it; + while (!isDone.f(i)) { + final String s = r.readLine(); + if (s == null) { + return i; } - return i; - }; - } - }; + final Input input = Input.el(s); + final F, IterV>, P1>> cont = F1Functions.lazy(Function.apply(input)); + i = i.fold(done, cont)._1(); + } + return i; + }; + } } /** @@ -209,91 +217,91 @@ public IO> f(final IterV it) { * (char[] of size {@link #DEFAULT_BUFFER_SIZE}). */ public static F, IO>>> charChunkReader() { - final F, Boolean> isDone = - new F, Boolean>() { - final F>, P1> done = constant(P.p(true)); - final F, IterV>, P1> cont = constant(P.p(false)); - - @Override - public Boolean f(final IterV i) { - return i.fold(done, cont)._1(); + return CharChunkReader::new; + } + + private static class CharChunkReader implements F, IO>> { + + private final Reader r; + + private final F, Boolean> isDone = i -> i.fold(constant(P.p(true)), constant(P.p(false)))._1(); + private final F>, P1>> done = errorF("iteratee is done"); //$NON-NLS-1$ + + CharChunkReader(Reader r) { + this.r = r; + } + + @Override + public IO> f(IterV it) { + // use loop instead of recursion because of missing TCO + return () -> { + + IterV i = it; + while (!isDone.f(i)) { + char[] buffer = new char[DEFAULT_BUFFER_SIZE]; + final int numRead = r.read(buffer); + if (numRead == -1) { + return i; } - }; - - return r -> new F, IO>>() { - final F>, P1>> done = errorF("iteratee is done"); //$NON-NLS-1$ - - @Override - public IO> f(final IterV it) { - // use loop instead of recursion because of missing TCO - return () -> { - - IterV i = it; - while (!isDone.f(i)) { - char[] buffer = new char[DEFAULT_BUFFER_SIZE]; - final int numRead = r.read(buffer); - if (numRead == -1) { - return i; - } - if (numRead < buffer.length) { - buffer = Arrays.copyOfRange(buffer, 0, numRead); - } - final Input input = Input.el(buffer); - final F, IterV>, P1>> cont = - F1Functions.lazy(Function.apply(input)); - i = i.fold(done, cont)._1(); + if (numRead < buffer.length) { + buffer = Arrays.copyOfRange(buffer, 0, numRead); } - return i; - }; - } - }; + final Input input = Input.el(buffer); + final F, IterV>, P1>> cont = + F1Functions.lazy(Function.apply(input)); + i = i.fold(done, cont)._1(); + } + return i; + }; + } } + + /** * A function that feeds an iteratee with characters read from a {@link Reader} * (chars are read in chunks of size {@link #DEFAULT_BUFFER_SIZE}). */ public static F, IO>>> charChunkReader2() { - final F, Boolean> isDone = - new F, Boolean>() { - final F>, P1> done = constant(P.p(true)); - final F, IterV>, P1> cont = constant(P.p(false)); - - @Override - public Boolean f(final IterV i) { - return i.fold(done, cont)._1(); + return CharChunkReader2::new; + } + + private static class CharChunkReader2 implements F, IO>> { + + private final Reader r; + + private final F, Boolean> isDone = i -> i.fold(constant(P.p(true)), constant(P.p(false)))._1(); + private final F>, IterV> done = errorF("iteratee is done"); //$NON-NLS-1$ + + CharChunkReader2(Reader r) { + this.r = r; + } + + @Override + public IO> f(IterV it) { + // use loop instead of recursion because of missing TCO + return () -> { + + IterV i = it; + while (!isDone.f(i)) { + char[] buffer = new char[DEFAULT_BUFFER_SIZE]; + final int numRead = r.read(buffer); + if (numRead == -1) { + return i; } - }; - - return r -> new F, IO>>() { - final F>, IterV> done = errorF("iteratee is done"); //$NON-NLS-1$ - - @Override - public IO> f(final IterV it) { - // use loop instead of recursion because of missing TCO - return () -> { - - IterV i = it; - while (!isDone.f(i)) { - char[] buffer = new char[DEFAULT_BUFFER_SIZE]; - final int numRead = r.read(buffer); - if (numRead == -1) { - return i; - } - if (numRead < buffer.length) { - buffer = Arrays.copyOfRange(buffer, 0, numRead); - } - for (char c : buffer) { - final Input input = Input.el(c); - final F, IterV>, IterV> cont = - Function.apply(input); - i = i.fold(done, cont); - } + if (numRead < buffer.length) { + buffer = Arrays.copyOfRange(buffer, 0, numRead); } - return i; - }; - } - }; + for (char c : buffer) { + final Input input = Input.el(c); + final F, IterV>, IterV> cont = + Function.apply(input); + i = i.fold(done, cont); + } + } + return i; + }; + } } public static IO map(final IO io, final F f) { diff --git a/core/src/main/java/fj/data/Java.java b/core/src/main/java/fj/data/Java.java index 487e9703..80a5ec43 100644 --- a/core/src/main/java/fj/data/Java.java +++ b/core/src/main/java/fj/data/Java.java @@ -404,27 +404,7 @@ public static F, SynchronousQueue> Array_SynchronousQueue(final * @return A function that converts streams to iterable. */ public static F, Iterable> Stream_Iterable() { - return as -> () -> new Iterator() { - private Stream x = as; - - public boolean hasNext() { - return x.isNotEmpty(); - } - - public A next() { - if (x.isEmpty()) - throw new NoSuchElementException("Empty iterator"); - else { - final A a = x.head(); - x = x.tail()._1(); - return a; - } - } - - public void remove() { - throw new UnsupportedOperationException(); - } - }; + return as -> as; } /** diff --git a/core/src/main/java/fj/data/TreeZipper.java b/core/src/main/java/fj/data/TreeZipper.java index 46392d20..9ffad4f4 100644 --- a/core/src/main/java/fj/data/TreeZipper.java +++ b/core/src/main/java/fj/data/TreeZipper.java @@ -628,21 +628,8 @@ private Stream>> uf(final F, Option F, P2, P1>>>> dwn() { - return tz -> P.p(tz, new P1>>() { - private F>, Option, Option>>>> fwd() { - return o -> { - Option, Option>>> r = none(); - for (final TreeZipper c : o) { - r = some(P.p(c, c.right())); - } - return r; - }; - } - - public Stream> _1() { - return unfold(fwd(), tz.firstChild()); - } - }); + F>, Option, Option>>>> fwd = o -> o.map(c -> P.p(c, c.right())); + return tz -> P.p(tz, P.lazy(() -> unfold(fwd, tz.firstChild()))); } /** diff --git a/demo/build.gradle b/demo/build.gradle index 41841cfa..b79cb246 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -17,4 +17,4 @@ test { jacoco { enabled = false } -} \ No newline at end of file +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 717f0389..72c40576 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Sat Aug 25 14:20:05 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip diff --git a/java-core/build.gradle b/java-core/build.gradle index 3b636072..b5ee71ec 100644 --- a/java-core/build.gradle +++ b/java-core/build.gradle @@ -16,4 +16,4 @@ configureUpload(signingEnabled, signModule) uploadArchives.enabled = true -configureAllRetroLambda() \ No newline at end of file +configureAllRetroLambda() diff --git a/props-core-scalacheck/build.gradle b/props-core-scalacheck/build.gradle index a395a586..10e494c9 100644 --- a/props-core-scalacheck/build.gradle +++ b/props-core-scalacheck/build.gradle @@ -4,7 +4,7 @@ archivesBaseName = "${project.projectName}-${project.name}" apply plugin: 'scala' ext { - scalaVersion = "2.11.11" + scalaVersion = "2.11.12" scalacheckScalaVersion = "2.11" scalacheckVersion = "1.12.6" signModule = true @@ -20,5 +20,3 @@ dependencies { performSigning(signingEnabled, signModule) configureUpload(signingEnabled, signModule) - - diff --git a/quickcheck/src/main/java/fj/test/Arbitrary.java b/quickcheck/src/main/java/fj/test/Arbitrary.java index df922af2..95bdedca 100644 --- a/quickcheck/src/main/java/fj/test/Arbitrary.java +++ b/quickcheck/src/main/java/fj/test/Arbitrary.java @@ -1,8 +1,6 @@ package fj.test; -import fj.Equal; import fj.F; -import fj.F1Functions; import fj.F2; import fj.F3; import fj.F4; @@ -13,7 +11,6 @@ import fj.Function; import fj.Bottom; -import static fj.Equal.longEqual; import static fj.Function.compose; import static fj.P.p; @@ -30,22 +27,14 @@ import fj.LcgRng; import fj.Ord; -import static fj.data.Either.left; -import static fj.data.Either.right; import static fj.data.Enumerator.charEnumerator; import static fj.data.List.asString; import static fj.data.List.list; -import static fj.data.Option.some; import fj.data.List; import fj.data.Set; -import fj.data.TreeMap; -import fj.function.Booleans; -import fj.function.Effect1; -import fj.function.Longs; import static fj.data.Stream.range; -import static fj.function.Booleans.not; import static fj.test.Gen.choose; import static fj.test.Gen.elements; import static fj.test.Gen.fail; @@ -95,11 +84,7 @@ public final class Arbitrary { * @return An arbitrary for functions. */ public static Gen> arbF(final Cogen c, final Gen a) { - return promote(new F>() { - public Gen f(final A x) { - return c.cogen(x, a); - } - }); + return promote(x -> c.cogen(x, a)); } public static Gen> arbReader(Cogen aa, Gen ab) { @@ -389,19 +374,14 @@ public static Gen> arbF4Invariant(final Gen * max, min, max - 1, min + 1) with a frequency of 1% each then generates from {@link * #arbInteger} the remainder of the time (93%). */ - public static final Gen arbIntegerBoundaries = sized(new F>() { - @SuppressWarnings("unchecked") - public Gen f(final Integer i) { - return frequency(list(p(1, value(0)), - p(1, value(1)), - p(1, value(-1)), - p(1, value(Integer.MAX_VALUE)), - p(1, value(Integer.MIN_VALUE)), - p(1, value(Integer.MAX_VALUE - 1)), - p(1, value(Integer.MIN_VALUE + 1)), - p(93, arbInteger))); - } - }); + public static final Gen arbIntegerBoundaries = sized(i -> frequency(list(p(1, value(0)), + p(1, value(1)), + p(1, value(-1)), + p(1, value(Integer.MAX_VALUE)), + p(1, value(Integer.MIN_VALUE)), + p(1, value(Integer.MAX_VALUE - 1)), + p(1, value(Integer.MIN_VALUE + 1)), + p(93, arbInteger)))); /** * An arbitrary implementation for long values. @@ -414,19 +394,14 @@ public Gen f(final Integer i) { * min, max - 1, min + 1) with a frequency of 1% each then generates from {@link #arbLong} * the remainder of the time (93%). */ - public static final Gen arbLongBoundaries = sized(new F>() { - @SuppressWarnings("unchecked") - public Gen f(final Integer i) { - return frequency(list(p(1, value(0L)), - p(1, value(1L)), - p(1, value(-1L)), - p(1, value(Long.MAX_VALUE)), - p(1, value(Long.MIN_VALUE)), - p(1, value(Long.MAX_VALUE - 1L)), - p(1, value(Long.MIN_VALUE + 1L)), - p(93, arbLong))); - } - }); + public static final Gen arbLongBoundaries = sized(i -> frequency(list(p(1, value(0L)), + p(1, value(1L)), + p(1, value(-1L)), + p(1, value(Long.MAX_VALUE)), + p(1, value(Long.MIN_VALUE)), + p(1, value(Long.MAX_VALUE - 1L)), + p(1, value(Long.MIN_VALUE + 1L)), + p(93, arbLong)))); /** * An arbitrary implementation for byte values. @@ -438,19 +413,14 @@ public Gen f(final Integer i) { * min, max - 1, min + 1) with a frequency of 1% each then generates from {@link #arbByte} * the remainder of the time (93%). */ - public static final Gen arbByteBoundaries = sized(new F>() { - @SuppressWarnings("unchecked") - public Gen f(final Integer i) { - return frequency(list(p(1, value((byte) 0)), - p(1, value((byte) 1)), - p(1, value((byte) -1)), - p(1, value(Byte.MAX_VALUE)), - p(1, value(Byte.MIN_VALUE)), - p(1, value((byte) (Byte.MAX_VALUE - 1))), - p(1, value((byte) (Byte.MIN_VALUE + 1))), - p(93, arbByte))); - } - }); + public static final Gen arbByteBoundaries = sized(i -> frequency(list(p(1, value((byte) 0)), + p(1, value((byte) 1)), + p(1, value((byte) -1)), + p(1, value(Byte.MAX_VALUE)), + p(1, value(Byte.MIN_VALUE)), + p(1, value((byte) (Byte.MAX_VALUE - 1))), + p(1, value((byte) (Byte.MIN_VALUE + 1))), + p(93, arbByte)))); /** * An arbitrary implementation for short values. @@ -462,19 +432,14 @@ public Gen f(final Integer i) { * min, max - 1, min + 1) with a frequency of 1% each then generates from {@link #arbShort} * the remainder of the time (93%). */ - public static final Gen arbShortBoundaries = sized(new F>() { - @SuppressWarnings("unchecked") - public Gen f(final Integer i) { - return frequency(list(p(1, value((short) 0)), - p(1, value((short) 1)), - p(1, value((short) -1)), - p(1, value(Short.MAX_VALUE)), - p(1, value(Short.MIN_VALUE)), - p(1, value((short) (Short.MAX_VALUE - 1))), - p(1, value((short) (Short.MIN_VALUE + 1))), - p(93, arbShort))); - } - }); + public static final Gen arbShortBoundaries = sized(i -> frequency(list(p(1, value((short) 0)), + p(1, value((short) 1)), + p(1, value((short) -1)), + p(1, value(Short.MAX_VALUE)), + p(1, value(Short.MIN_VALUE)), + p(1, value((short) (Short.MAX_VALUE - 1))), + p(1, value((short) (Short.MIN_VALUE + 1))), + p(93, arbShort)))); /** * An arbitrary implementation for character values. @@ -486,16 +451,11 @@ public Gen f(final Integer i) { * max - 1, min + 1) with a frequency of 1% each then generates from {@link #arbCharacter} * the remainder of the time (96%). */ - public static final Gen arbCharacterBoundaries = sized(new F>() { - @SuppressWarnings("unchecked") - public Gen f(final Integer i) { - return frequency(list(p(1, value(Character.MIN_VALUE)), - p(1, value((char) (Character.MIN_VALUE + 1))), - p(1, value(Character.MAX_VALUE)), - p(1, value((char) (Character.MAX_VALUE - 1))), - p(95, arbCharacter))); - } - }); + public static final Gen arbCharacterBoundaries = sized(i -> frequency(list(p(1, value(Character.MIN_VALUE)), + p(1, value((char) (Character.MIN_VALUE + 1))), + p(1, value(Character.MAX_VALUE)), + p(1, value((char) (Character.MAX_VALUE - 1))), + p(95, arbCharacter)))); /** * An arbitrary implementation for double values. @@ -507,21 +467,16 @@ public Gen f(final Integer i) { * min, min (normal), NaN, -infinity, infinity, max - 1) with a frequency of 1% each then * generates from {@link #arbDouble} the remainder of the time (91%). */ - public static final Gen arbDoubleBoundaries = sized(new F>() { - @SuppressWarnings("unchecked") - public Gen f(final Integer i) { - return frequency(list(p(1, value(0D)), - p(1, value(1D)), - p(1, value(-1D)), - p(1, value(Double.MAX_VALUE)), - p(1, value(Double.MIN_VALUE)), - p(1, value(Double.NaN)), - p(1, value(Double.NEGATIVE_INFINITY)), - p(1, value(Double.POSITIVE_INFINITY)), - p(1, value(Double.MAX_VALUE - 1D)), - p(91, arbDouble))); - } - }); + public static final Gen arbDoubleBoundaries = sized(i -> frequency(list(p(1, value(0D)), + p(1, value(1D)), + p(1, value(-1D)), + p(1, value(Double.MAX_VALUE)), + p(1, value(Double.MIN_VALUE)), + p(1, value(Double.NaN)), + p(1, value(Double.NEGATIVE_INFINITY)), + p(1, value(Double.POSITIVE_INFINITY)), + p(1, value(Double.MAX_VALUE - 1D)), + p(91, arbDouble)))); /** * An arbitrary implementation for float values. @@ -533,21 +488,16 @@ public Gen f(final Integer i) { * min, NaN, -infinity, infinity, max - 1) with a frequency of 1% each then generates from * {@link #arbFloat} the remainder of the time (91%). */ - public static final Gen arbFloatBoundaries = sized(new F>() { - @SuppressWarnings("unchecked") - public Gen f(final Integer i) { - return frequency(list(p(1, value(0F)), - p(1, value(1F)), - p(1, value(-1F)), - p(1, value(Float.MAX_VALUE)), - p(1, value(Float.MIN_VALUE)), - p(1, value(Float.NaN)), - p(1, value(Float.NEGATIVE_INFINITY)), - p(1, value(Float.POSITIVE_INFINITY)), - p(1, value(Float.MAX_VALUE - 1F)), - p(91, arbFloat))); - } - }); + public static final Gen arbFloatBoundaries = sized(i -> frequency(list(p(1, value(0F)), + p(1, value(1F)), + p(1, value(-1F)), + p(1, value(Float.MAX_VALUE)), + p(1, value(Float.MIN_VALUE)), + p(1, value(Float.NaN)), + p(1, value(Float.NEGATIVE_INFINITY)), + p(1, value(Float.POSITIVE_INFINITY)), + p(1, value(Float.MAX_VALUE - 1F)), + p(91, arbFloat)))); /** * An arbitrary implementation for string values. @@ -670,7 +620,7 @@ public static Gen> arbNonEmptyList(final Gen aa) { * Returns an arbitrary Validation for the given arbitrary parameters. */ public static Gen> arbValidation(final Gen aa, final Gen ab) { - return arbBoolean.bind(bool -> bool ? ab.map(Validation::success) : aa.map(Validation::fail)); + return arbBoolean.bind(bool -> bool ? ab.map(Validation::success) : aa.map(Validation::fail)); } /** @@ -690,7 +640,7 @@ public static Gen> arbStream(final Gen aa) { * @return An arbitrary implementation for arrays. */ public static Gen> arbArray(final Gen aa) { - return arbList(aa).map(List::toArray); + return arbList(aa).map(List::toArray); } /** @@ -1029,8 +979,8 @@ public static Gen> arbWeakHashMap(final Gen ak, fina */ public static Gen> arbArrayBlockingQueue(final Gen aa) { return arbArray(aa).bind(arbInteger, arbBoolean, - a -> capacity -> fair -> new ArrayBlockingQueue(a.length() + abs(capacity), - fair, a.asJavaList())); + a -> capacity -> fair -> new ArrayBlockingQueue<>(a.length() + abs(capacity), + fair, a.asJavaList())); } /** diff --git a/quickcheck/src/main/java/fj/test/Arg.java b/quickcheck/src/main/java/fj/test/Arg.java index 4187c15d..71df9e2d 100644 --- a/quickcheck/src/main/java/fj/test/Arg.java +++ b/quickcheck/src/main/java/fj/test/Arg.java @@ -1,6 +1,5 @@ package fj.test; -import fj.F; import fj.Show; import static fj.Show.anyShow; @@ -52,10 +51,6 @@ public int shrinks() { /** * The rendering of an argument (uses {@link Object#toString()} for the argument value). */ - public static final Show> argShow = showS(new F, String>() { - public String f(final Arg arg) { - return anyShow().showS(arg.value) + - (arg.shrinks > 0 ? " (" + arg.shrinks + " shrink" + (arg.shrinks == 1 ? "" : 's') + ')' : ""); - } - }); + public static final Show> argShow = showS(arg -> anyShow().showS(arg.value) + + (arg.shrinks > 0 ? " (" + arg.shrinks + " shrink" + (arg.shrinks == 1 ? "" : 's') + ')' : "")); } diff --git a/quickcheck/src/main/java/fj/test/CheckResult.java b/quickcheck/src/main/java/fj/test/CheckResult.java index c9c0a2fe..784fe4e9 100644 --- a/quickcheck/src/main/java/fj/test/CheckResult.java +++ b/quickcheck/src/main/java/fj/test/CheckResult.java @@ -286,16 +286,14 @@ else if (r.isPropException()) { * the result is a failure (falsified, property exception or generator exception). */ public static Show summaryEx(final Show> sa) { - return showS(new F() { - public String f(final CheckResult r) { - final String s = summary(sa).showS(r); - if (r.isProven() || r.isPassed() || r.isExhausted()) - return s; - else if (r.isFalsified() || r.isPropException() || r.isGenException()) - throw new Error(s); - else - throw decons(r.getClass()); - } + return showS(r -> { + final String s = summary(sa).showS(r); + if (r.isProven() || r.isPassed() || r.isExhausted()) + return s; + else if (r.isFalsified() || r.isPropException() || r.isGenException()) + throw new Error(s); + else + throw decons(r.getClass()); }); } } diff --git a/quickcheck/src/main/java/fj/test/Cogen.java b/quickcheck/src/main/java/fj/test/Cogen.java index c4c3b0a0..5770ea21 100644 --- a/quickcheck/src/main/java/fj/test/Cogen.java +++ b/quickcheck/src/main/java/fj/test/Cogen.java @@ -7,7 +7,6 @@ import fj.data.*; -import static fj.data.Array.array; import static fj.data.Array.iterableArray; import static fj.data.List.fromString; import static fj.data.List.nil; @@ -501,11 +500,7 @@ public Gen cogen(final Array as, final Gen g) { * @return A cogen for throwables. */ public static Cogen cogenThrowable(final Cogen cs) { - return cs.contramap(new F() { - public String f(final Throwable t) { - return t.getMessage(); - } - }); + return cs.contramap(Throwable::getMessage); } /** diff --git a/quickcheck/src/main/java/fj/test/Gen.java b/quickcheck/src/main/java/fj/test/Gen.java index 5b0a98bc..a5e30a9d 100644 --- a/quickcheck/src/main/java/fj/test/Gen.java +++ b/quickcheck/src/main/java/fj/test/Gen.java @@ -275,7 +275,7 @@ public Gen bind(final Gen gb, final Gen gc, final Gen g * @return A new generator after function application. */ public Gen apply(final Gen> gf) { - return gf.bind(f1 -> map(f1)); + return gf.bind(this::map); } /** diff --git a/quickcheck/src/main/java/fj/test/Property.java b/quickcheck/src/main/java/fj/test/Property.java index 0f77f85e..2a327ebc 100644 --- a/quickcheck/src/main/java/fj/test/Property.java +++ b/quickcheck/src/main/java/fj/test/Property.java @@ -501,11 +501,7 @@ public static Property property(final Gen aa, final F f) { * application of its arguments. */ public static Property propertyP(final Gen aa, final Gen ab, final Shrink sa, final Shrink sb, final F>> f) { - return property(aa, sa, a -> { - return propertyP(ab, sb, b -> { - return f.f(a).f(b); - }); - }); + return property(aa, sa, a -> propertyP(ab, sb, b -> f.f(a).f(b))); } /** @@ -633,9 +629,7 @@ public static Property property(final Gen aa, final Shrink sb, final Shrink sc, final F>> f) { - return property(aa, ab, sa, sb, a -> b -> property(ac, sc, c -> { - return f.f(a).f(b).f(c); - })); + return property(aa, ab, sa, sb, a -> b -> property(ac, sc, c -> f.f(a).f(b).f(c))); } /** @@ -723,9 +717,7 @@ public static Property property(final Gen aa, final Shrink sc, final Shrink sd, final F>>> f) { - return property(aa, ab, ac, sa, sb, sc, a -> b -> c -> property(ad, sd, d -> { - return f.f(a).f(b).f(c).f(d); - })); + return property(aa, ab, ac, sa, sb, sc, a -> b -> c -> property(ad, sd, d -> f.f(a).f(b).f(c).f(d))); } /** @@ -825,9 +817,7 @@ public static Property property(final Gen aa, final Shrink sd, final Shrink se, final F>>>> f) { - return property(aa, ab, ac, ad, sa, sb, sc, sd, a -> b -> c -> d -> property(ae, se, e -> { - return f.f(a).f(b).f(c).f(d).f(e); - })); + return property(aa, ab, ac, ad, sa, sb, sc, sd, a -> b -> c -> d -> property(ae, se, e -> f.f(a).f(b).f(c).f(d).f(e))); } /** @@ -939,9 +929,7 @@ public static Property property(final Gen aa, final Shrink se, final Shrink sf, final F>>>>> f) { - return property(aa, ab, ac, ad, ae, sa, sb, sc, sd, se, a -> b -> c -> d -> e -> property(af, sf, f$ -> { - return f.f(a).f(b).f(c).f(d).f(e).f(f$); - })); + return property(aa, ab, ac, ad, ae, sa, sb, sc, sd, se, a -> b -> c -> d -> e -> property(af, sf, f$ -> f.f(a).f(b).f(c).f(d).f(e).f(f$))); } /** @@ -1065,9 +1053,7 @@ public static Property property(final Gen aa, final Shrink sf, final Shrink sg, final F>>>>>> f) { - return property(aa, ab, ac, ad, ae, af, sa, sb, sc, sd, se, sf, a -> b -> c -> d -> e -> f$ -> property(ag, sg, g -> { - return f.f(a).f(b).f(c).f(d).f(e).f(f$).f(g); - })); + return property(aa, ab, ac, ad, ae, af, sa, sb, sc, sd, se, sf, a -> b -> c -> d -> e -> f$ -> property(ag, sg, g -> f.f(a).f(b).f(c).f(d).f(e).f(f$).f(g))); } /** @@ -1203,9 +1189,7 @@ public static Property property(final Gen aa, final Shrink sg, final Shrink sh, final F>>>>>>> f) { - return property(aa, ab, ac, ad, ae, af, ag, sa, sb, sc, sd, se, sf, sg, a -> b -> c -> d -> e -> f$ -> g -> property(ah, sh, h -> { - return f.f(a).f(b).f(c).f(d).f(e).f(f$).f(g).f(h); - })); + return property(aa, ab, ac, ad, ae, af, ag, sa, sb, sc, sd, se, sf, sg, a -> b -> c -> d -> e -> f$ -> g -> property(ah, sh, h -> f.f(a).f(b).f(c).f(d).f(e).f(f$).f(g).f(h))); } /** diff --git a/quickcheck/src/main/java/fj/test/Shrink.java b/quickcheck/src/main/java/fj/test/Shrink.java index 77c95381..483293e8 100644 --- a/quickcheck/src/main/java/fj/test/Shrink.java +++ b/quickcheck/src/main/java/fj/test/Shrink.java @@ -23,7 +23,7 @@ import static fj.Primitive.Long_Integer; import static fj.Primitive.Long_Short; import static fj.Primitive.Short_Long; -import static fj.data.Array.array; + import fj.data.Conversions; import static fj.data.List.isNotEmpty_; import fj.data.Array; @@ -38,7 +38,6 @@ import static fj.data.Stream.iterate; import static fj.data.Stream.nil; -import static java.lang.System.arraycopy; import static java.math.BigInteger.ZERO; import java.math.BigDecimal; @@ -683,7 +682,7 @@ public static Shrink> shrinkSynchronousQueue(final Shrin final Stream is = cons(ZERO, () -> iterate(x -> x.divide(two), i) .takeWhile(x2 -> eq.notEq(x2, ZERO)) - .map(i::subtract)); + .map(x2 -> i.subtract(x2))); return Ord.bigintOrd.isLessThan(i, ZERO) ? cons(i.negate(), () -> is) : is; } diff --git a/quickcheck/src/main/java/fj/test/reflect/Check.java b/quickcheck/src/main/java/fj/test/reflect/Check.java index eb865e04..a1eea79b 100644 --- a/quickcheck/src/main/java/fj/test/reflect/Check.java +++ b/quickcheck/src/main/java/fj/test/reflect/Check.java @@ -1,6 +1,5 @@ package fj.test.reflect; -import fj.Class; import static fj.Class.clas; import fj.F; import fj.Function; @@ -24,8 +23,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; + import static java.lang.reflect.Modifier.isStatic; /** @@ -177,7 +175,7 @@ public static List> check(final java.lang.Class c */ public static List>> properties(final java.lang.Class c, final String... categories) { //noinspection ClassEscapesDefinedScope - final Array>> propFields = properties(array(c.getDeclaredFields()).map((F) f -> new PropertyMember() { + final Array>> propFields = properties(array(c.getDeclaredFields()).map(f -> new PropertyMember() { public java.lang.Class type() { return f.getType(); } @@ -205,7 +203,7 @@ public boolean isProperty() { }), c, categories); //noinspection ClassEscapesDefinedScope - final Array>> propMethods = properties(array(c.getDeclaredMethods()).map((F) m -> { + final Array>> propMethods = properties(array(c.getDeclaredMethods()).map(m -> { //noinspection ProhibitedExceptionDeclared return new PropertyMember() { public java.lang.Class type() { diff --git a/quickcheck/src/main/java/fj/test/reflect/CheckParams.java b/quickcheck/src/main/java/fj/test/reflect/CheckParams.java index 8c8445c1..7951b40e 100644 --- a/quickcheck/src/main/java/fj/test/reflect/CheckParams.java +++ b/quickcheck/src/main/java/fj/test/reflect/CheckParams.java @@ -1,5 +1,6 @@ package fj.test.reflect; +import fj.F0; import fj.test.Property; import java.lang.annotation.Documented; diff --git a/quickcheck/src/main/java/fj/test/reflect/Main.java b/quickcheck/src/main/java/fj/test/reflect/Main.java index 5f6f3dfc..b07997c2 100644 --- a/quickcheck/src/main/java/fj/test/reflect/Main.java +++ b/quickcheck/src/main/java/fj/test/reflect/Main.java @@ -1,10 +1,7 @@ package fj.test.reflect; -import fj.P2; import static fj.data.Array.array; -import fj.function.Effect1; -import fj.test.CheckResult; import static fj.test.CheckResult.summary; import static fj.test.reflect.Check.check; diff --git a/quickcheck/src/main/java/fj/test/runner/PropertyTestRunner.java b/quickcheck/src/main/java/fj/test/runner/PropertyTestRunner.java index 876dbff1..8b90d57c 100644 --- a/quickcheck/src/main/java/fj/test/runner/PropertyTestRunner.java +++ b/quickcheck/src/main/java/fj/test/runner/PropertyTestRunner.java @@ -58,11 +58,10 @@ private static String getLabel(Description d) { } private static CheckResult checkProperty(Property prop, Option params) { - for (CheckParams ps : params) { - return prop.check(ps.minSuccessful(), ps.maxDiscarded(), ps.minSize(), ps.maxSize()); - } - - return prop.check(); + return params.option( + prop::check, + ps -> prop.check(ps.minSuccessful(), ps.maxDiscarded(), ps.minSize(), ps.maxSize()) + ); } @Override diff --git a/quickcheck/src/test/java/fj/data/test/TestCheck.java b/quickcheck/src/test/java/fj/data/test/TestCheck.java index 935f1127..3a4e6a80 100644 --- a/quickcheck/src/test/java/fj/data/test/TestCheck.java +++ b/quickcheck/src/test/java/fj/data/test/TestCheck.java @@ -1,16 +1,10 @@ package fj.data.test; -import fj.F2; -import fj.F3; -import fj.data.List; -import fj.test.Arbitrary; import fj.test.CheckResult; import fj.test.Gen; import fj.test.Property; import org.junit.*; -import static fj.Function.compose; -import static fj.test.Arbitrary.arbLong; import static fj.test.Property.prop; import static fj.test.Property.property; import static org.junit.Assert.*; @@ -19,13 +13,11 @@ public class TestCheck { @Test(timeout=5000 /*ms*/) public void testExceptionsThrownFromGeneratorsArePropagated() { - Gen failingGen = Gen.value(0).map((i) -> { + Gen failingGen = Gen.value(0).map((i) -> { throw new RuntimeException("test failure"); }); - Property p = property(failingGen, (Integer i) -> { - return prop(i == 0); - }); + Property p = property(failingGen, (Integer i) -> prop(i == 0)); CheckResult res = p.check( 1, /*minSuccessful*/ diff --git a/quickcheck/src/test/java/fj/data/test/TestNull.java b/quickcheck/src/test/java/fj/data/test/TestNull.java index 0fd44c6a..fe9dc843 100644 --- a/quickcheck/src/test/java/fj/data/test/TestNull.java +++ b/quickcheck/src/test/java/fj/data/test/TestNull.java @@ -1,16 +1,10 @@ package fj.data.test; -import fj.F2; -import fj.F3; -import fj.data.List; -import fj.test.Arbitrary; import fj.test.CheckResult; import fj.test.Gen; import fj.test.Property; import org.junit.Test; -import static fj.Function.compose; -import static fj.test.Arbitrary.arbLong; import static fj.test.Property.prop; import static fj.test.Property.property; @@ -21,9 +15,7 @@ public class TestNull { @Test public void testShowNullParameters() { - Property p = property(Gen.value(null), (Integer i) -> { - return prop(i != null); - }); + Property p = property(Gen.value(null), (Integer i) -> prop(i != null)); CheckResult.summary.println(p.check()); } diff --git a/quickcheck/src/test/java/fj/test/GenTest.java b/quickcheck/src/test/java/fj/test/GenTest.java index 3bb723b8..900369a7 100644 --- a/quickcheck/src/test/java/fj/test/GenTest.java +++ b/quickcheck/src/test/java/fj/test/GenTest.java @@ -22,9 +22,7 @@ public final class GenTest { @Test public void testCombinationOf_none() { Gen> instance = combinationOf(0, AS); - testPick(100, instance, actual -> { - assertTrue(actual.isEmpty()); - }); + testPick(100, instance, actual -> assertTrue(actual.isEmpty())); } @Test @@ -59,9 +57,7 @@ public void testCombinationOf_three() { @Test public void testSelectionOf_none() { Gen> instance = selectionOf(0, AS); - testPick(100, instance, actual -> { - assertTrue(actual.isEmpty()); - }); + testPick(100, instance, actual -> assertTrue(actual.isEmpty())); } @Test @@ -106,9 +102,7 @@ public void testSelectionOf_four() { @Test public void testPermutationOf_none() { Gen> instance = permutationOf(0, AS); - testPick(100, instance, actual -> { - assertTrue(actual.isEmpty()); - }); + testPick(100, instance, actual -> assertTrue(actual.isEmpty())); } @Test @@ -142,9 +136,7 @@ public void testPermutationOf_three() { @Test public void testWordOf_none() { Gen> instance = wordOf(0, AS); - testPick(100, instance, actual -> { - assertTrue(actual.isEmpty()); - }); + testPick(100, instance, actual -> assertTrue(actual.isEmpty())); } @Test @@ -184,7 +176,7 @@ public void testWordOf_four() { } private static void testPick(int n, Gen> instance, Effect1> test) { - range(0, n).map(ignore -> instance.gen(0, standard)).foreachDoEffect(test::f); + range(0, n).map(ignore -> instance.gen(0, standard)).foreachDoEffect(test); } } From 5b6b88979c717c03d47d9d3977912df9b7f72153 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sat, 25 Aug 2018 21:16:44 +0200 Subject: [PATCH 052/173] Initiate 5.x series: build with jdk11 by default. Move java8 to core. --- .travis.yml | 11 +++-- build.gradle | 22 ++++----- core/build.gradle | 2 - .../src/main/java/fj/data/Collectors.java | 0 .../src/main/java/fj/data/Java8.java | 22 ++++----- demo/build.gradle | 2 - java-core/build.gradle | 2 - java8/build.gradle | 18 ------- java8/src/test/java/fj/EmptyTest.java | 17 ------- lib.gradle | 24 ---------- performance/build.gradle | 2 - props-core/build.gradle | 2 - quickcheck/build.gradle | 2 - .../src/main/java/fj/test/Arbitrary.java | 48 +++++++++---------- settings.gradle | 2 +- 15 files changed, 52 insertions(+), 124 deletions(-) rename {java8 => core}/src/main/java/fj/data/Collectors.java (100%) rename {java8 => core}/src/main/java/fj/data/Java8.java (97%) delete mode 100644 java8/build.gradle delete mode 100644 java8/src/test/java/fj/EmptyTest.java diff --git a/.travis.yml b/.travis.yml index 7c55b120..bbe5966b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,19 +4,24 @@ sudo: false language: java jdk: - - openjdk11 + - openjdk10 + - openjdk-ea matrix: include: - - jdk: openjdk8 + - jdk: openjdk11 script: - ./gradlew build coverage -s -i after_success: - bash <(curl -s https://codecov.io/bash) - - '[ "$TRAVIS_BRANCH" = "series/4.x" -a "$TRAVIS_PULL_REQUEST" = "false" -a -z "$TRAVIS_TAG" ] + - '[ "$TRAVIS_BRANCH" = "series/5.x" -a "$TRAVIS_PULL_REQUEST" = "false" -a -z "$TRAVIS_TAG" ] && ./gradlew uploadArchives' + allow_failures: + - jdk: openjdk10 + - jdk: openjdk-ea + script: - ./gradlew clean test diff --git a/build.gradle b/build.gradle index 9d98d2b3..b386b5d3 100644 --- a/build.gradle +++ b/build.gradle @@ -7,8 +7,6 @@ ext { buildscript { ext { uptodateVersion = "1.6.3" - retrolambdaPluginVersion = "3.7.0" - retrolambdaVersion = "2.5.4" } repositories { @@ -19,7 +17,6 @@ buildscript { dependencies { classpath "com.ofg:uptodate-gradle-plugin:$uptodateVersion" - classpath "me.tatarka:gradle-retrolambda:$retrolambdaPluginVersion" } } @@ -43,14 +40,13 @@ allprojects { ext { isSnapshot = true - fjBaseVersion = "4.9" + fjBaseVersion = "5.0" snapshotAppendix = "-SNAPSHOT" fjVersion = fjBaseVersion + (isSnapshot ? snapshotAppendix : "") fjConsumeVersion = "4.8" signModule = false - useRetroLambda = false projectTitle = "Functional Java" projectName = "functionaljava" @@ -71,10 +67,6 @@ allprojects { dependencyJunit = "org.junit.vintage:junit-vintage-engine:5.2.0" displayCompilerWarnings = true - - newJdkEnvVar = "JAVA8_HOME" - oldJdkEnvVar = "JAVA7_HOME" - retroLambdaTarget = JavaVersion.VERSION_1_6 } repositories { @@ -111,11 +103,13 @@ subprojects { } } - if (displayCompilerWarnings) { - tasks.withType(JavaCompile) { + + tasks.withType(JavaCompile) { + options.compilerArgs.addAll(['--release', '10']) + if (displayCompilerWarnings) { options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" - } - } + } + } jacocoTestReport { additionalSourceDirs = files(sourceSets.main.allSource.srcDirs) @@ -136,7 +130,7 @@ task coverage(type: org.gradle.testing.jacoco.tasks.JacocoReport) { dependsOn = subprojects.coverage executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec") // We only care about coverage of: - def projectForFoverage = ["core", "java8", "quickcheck", "java-core"] + def projectForFoverage = ["core", "quickcheck", "java-core"] classDirectories = files(subprojects.findAll {subproject -> subproject.name in projectForFoverage} .sourceSets.main.output) sourceDirectories = files(subprojects.findAll {subproject -> subproject.name in projectForFoverage} .sourceSets.main.allSource.srcDirs) diff --git a/core/build.gradle b/core/build.gradle index 71dae913..13d06114 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -15,5 +15,3 @@ performSigning(signingEnabled, signModule) configureUpload(signingEnabled, signModule) uploadArchives.enabled = true - -configureAllRetroLambda() diff --git a/java8/src/main/java/fj/data/Collectors.java b/core/src/main/java/fj/data/Collectors.java similarity index 100% rename from java8/src/main/java/fj/data/Collectors.java rename to core/src/main/java/fj/data/Collectors.java diff --git a/java8/src/main/java/fj/data/Java8.java b/core/src/main/java/fj/data/Java8.java similarity index 97% rename from java8/src/main/java/fj/data/Java8.java rename to core/src/main/java/fj/data/Java8.java index e41f0958..46c5ca2a 100644 --- a/java8/src/main/java/fj/data/Java8.java +++ b/core/src/main/java/fj/data/Java8.java @@ -1,14 +1,5 @@ package fj.data; -import java.util.Iterator; -import java.util.Optional; -import java.util.Spliterators; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.StreamSupport; - import fj.F; import fj.F2; import fj.P; @@ -19,6 +10,15 @@ import fj.function.Try1; import fj.function.Try2; +import java.util.Iterator; +import java.util.Optional; +import java.util.Spliterators; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.StreamSupport; + /** * Created by mperry on 3/06/2014. */ @@ -134,7 +134,7 @@ public static F Consumer_F(final Consumer c) { }; } - public static java.util.stream.Stream Stream_JavaStream(final fj.data.Stream s) { + public static java.util.stream.Stream Stream_JavaStream(final Stream s) { return Iterable_JavaStream(s); } @@ -146,7 +146,7 @@ public static java.util.stream.Stream Iterator_JavaStream(final Iterator< return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false); } - public static F, java.util.stream.Stream> Stream_JavaStream() { + public static F, java.util.stream.Stream> Stream_JavaStream() { return Java8::Stream_JavaStream; } diff --git a/demo/build.gradle b/demo/build.gradle index b79cb246..e97f94b8 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -5,8 +5,6 @@ mainClassName = "fj.demo.euler.Problem2" archivesBaseName = "${project.projectName}-${project.name}" -configureAllRetroLambda() - dependencies { compile project(":core") compile project(":quickcheck") diff --git a/java-core/build.gradle b/java-core/build.gradle index b5ee71ec..22bf8970 100644 --- a/java-core/build.gradle +++ b/java-core/build.gradle @@ -15,5 +15,3 @@ configureUpload(signingEnabled, signModule) uploadArchives.enabled = true - -configureAllRetroLambda() diff --git a/java8/build.gradle b/java8/build.gradle deleted file mode 100644 index d6d4c485..00000000 --- a/java8/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ - -archivesBaseName = "${project.projectName}-${project.name}" - -ext { - signModule = true -} - -dependencies { - compile project(":core") - testCompile dependencyJunit -} - -performSigning(signingEnabled, signModule) -configureUpload(signingEnabled, signModule) - -if (!useRetroLambda) { - uploadArchives.enabled = true -} diff --git a/java8/src/test/java/fj/EmptyTest.java b/java8/src/test/java/fj/EmptyTest.java deleted file mode 100644 index e187cccb..00000000 --- a/java8/src/test/java/fj/EmptyTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package fj; - -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; - -/** - * Created by MarkPerry on 30/08/2015. - */ -public class EmptyTest { - - @Ignore @Test - public void missing() { - Assert.fail("not implemented"); - - } -} diff --git a/lib.gradle b/lib.gradle index 253ed251..39ce7a7e 100644 --- a/lib.gradle +++ b/lib.gradle @@ -77,33 +77,9 @@ void configureUpload(String signingEnabled, Boolean signModule) { } } -void configureAllRetroLambda() { - configureRetroLambda(useRetroLambda, newJdkEnvVar, oldJdkEnvVar, retroLambdaTarget) -} - -void configureRetroLambda(boolean useRetroLambda, String newJdkEnvVar, String oldJdkEnvVar, JavaVersion retroLambdaTarget) { - - if (useRetroLambda) { - apply plugin: 'me.tatarka.retrolambda' - retrolambda { - jdk System.getenv(newJdkEnvVar) - oldJdk System.getenv(oldJdkEnvVar) - javaVersion retroLambdaTarget - defaultMethods true - } - dependencies { - retrolambdaConfig "net.orfjackal.retrolambda:retrolambda:$retrolambdaVersion" - } - } else { - project.archivesBaseName = "${project.archivesBaseName}_1.8" - } -} - ext { findJavaCommand = this.&findJavaCommand doSigning = this.&doSigning performSigning = this.&performSigning configureUpload = this.&configureUpload - configureRetroLambda = this.&configureRetroLambda - configureAllRetroLambda = this.&configureAllRetroLambda } diff --git a/performance/build.gradle b/performance/build.gradle index b5bc2916..cac2f584 100644 --- a/performance/build.gradle +++ b/performance/build.gradle @@ -1,6 +1,4 @@ -configureAllRetroLambda() - dependencies { compile project(":core") testCompile dependencyJunit diff --git a/props-core/build.gradle b/props-core/build.gradle index d697cff5..181ae5b2 100644 --- a/props-core/build.gradle +++ b/props-core/build.gradle @@ -1,8 +1,6 @@ archivesBaseName = "${project.projectName}-${project.name}" -configureAllRetroLambda() - dependencies { compile project(":quickcheck") testCompile dependencyJunit diff --git a/quickcheck/build.gradle b/quickcheck/build.gradle index 45e379da..b477e26a 100644 --- a/quickcheck/build.gradle +++ b/quickcheck/build.gradle @@ -5,8 +5,6 @@ ext { archivesBaseName = "${project.projectName}-${project.name}" -configureAllRetroLambda() - dependencies { compile project(":core") compile dependencyJunit diff --git a/quickcheck/src/main/java/fj/test/Arbitrary.java b/quickcheck/src/main/java/fj/test/Arbitrary.java index 95bdedca..8e5461fd 100644 --- a/quickcheck/src/main/java/fj/test/Arbitrary.java +++ b/quickcheck/src/main/java/fj/test/Arbitrary.java @@ -260,13 +260,13 @@ public static Gen> arbF4Invariant(final Gen * @return An arbitrary for function-6. */ public static Gen> arbF6Invariant(final Gen a) { - return a.map(compose(Function.uncurryF6(), - compose(Function.>>>>>constant(), - compose(Function.>>>>constant(), - compose(Function.>>>constant(), - compose(Function.>>constant(), - compose(Function.>constant(), - Function.constant()))))))); + return a.map(compose(Function.uncurryF6(), + compose(Function.constant(), + compose(Function.constant(), + compose(Function.constant(), + compose(Function.constant(), + compose(Function.constant(), + Function.constant()))))))); } /** @@ -301,14 +301,14 @@ public static Gen> arbF4Invariant(final Gen * @return An arbitrary for function-7. */ public static Gen> arbF7Invariant(final Gen a) { - return a.map(compose(Function.uncurryF7(), - compose(Function.>>>>>>constant(), - compose(Function.>>>>>constant(), - compose(Function.>>>>constant(), - compose(Function.>>>constant(), - compose(Function.>>constant(), - compose(Function.>constant(), - Function.constant())))))))); + return a.map(compose(Function.uncurryF7(), + compose(Function.constant(), + compose(Function.constant(), + compose(Function.constant(), + compose(Function.constant(), + compose(Function.constant(), + compose(Function.constant(), + Function.constant())))))))); } /** @@ -346,17 +346,17 @@ public static Gen> arbF4Invariant(final Gen */ public static Gen> arbF8Invariant( final Gen a) { - return a.map(compose(Function.uncurryF8(), - compose(Function.>>>>>>>constant(), - compose(Function.>>>>>>constant(), - compose(Function.>>>>>constant(), + return a.map(compose(Function.uncurryF8(), + compose(Function.constant(), + compose(Function.constant(), + compose(Function.constant(), compose( - Function.>>>>constant(), - compose(Function.>>>constant(), + Function.constant(), + compose(Function.constant(), compose( - Function.>>constant(), - compose(Function.>constant(), - Function.constant()))))))))); + Function.constant(), + compose(Function.constant(), + Function.constant()))))))))); } /** diff --git a/settings.gradle b/settings.gradle index d231d998..f1a6bd56 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ rootProject.name = "functionaljava" -include "core", "demo", "consume", "java8", "quickcheck", "props-core", "props-core-scalacheck", "java-core", "performance" +include "core", "demo", "consume", "quickcheck", "props-core", "props-core-scalacheck", "java-core", "performance" From 2e1f2c57176a2bf8974ef0725e681e9042cff9ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sun, 23 Sep 2018 14:28:40 -0400 Subject: [PATCH 053/173] Add Strategy tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- .../fj/control/parallel/StrategyTest.java | 61 +++++++++++++++++++ core/src/test/java/fj/data/StreamTest.java | 16 ----- 2 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 core/src/test/java/fj/control/parallel/StrategyTest.java diff --git a/core/src/test/java/fj/control/parallel/StrategyTest.java b/core/src/test/java/fj/control/parallel/StrategyTest.java new file mode 100644 index 00000000..ee3b30df --- /dev/null +++ b/core/src/test/java/fj/control/parallel/StrategyTest.java @@ -0,0 +1,61 @@ +package fj.control.parallel; + +import fj.Ord; +import fj.P; +import fj.P1; +import fj.data.Enumerator; +import fj.data.List; +import fj.data.Stream; +import org.junit.Test; + +import java.util.concurrent.*; + +import static fj.control.parallel.Callables.callable; +import static fj.control.parallel.Strategy.*; +import static fj.data.Stream.range; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class StrategyTest { + + @Test + public void testStrategySeq() { + final Stream s = range(Enumerator.intEnumerator, 99, -99, -1); + assertThat(s.sort(Ord.intOrd, seqStrategy()), is(s.sort(Ord.intOrd))); + } + + @Test + public void testStrategyThread() { + final Stream s = range(Enumerator.intEnumerator, 99, -99, -1); + assertThat(s.sort(Ord.intOrd, simpleThreadStrategy()), is(s.sort(Ord.intOrd))); + } + + @Test + public void testStrategyExecutor() { + final Stream s = range(Enumerator.intEnumerator, 99, -99, -1); + final ExecutorService es = Executors.newFixedThreadPool(10); + assertThat(s.sort(Ord.intOrd, executorStrategy(es)), is(s.sort(Ord.intOrd))); + } + + @Test + public void testStrategyCompletion() { + final Stream s = range(Enumerator.intEnumerator, 99, -99, -1); + final ExecutorService es = Executors.newFixedThreadPool(10); + final CompletionService cs = new ExecutorCompletionService(es); + assertThat(s.sort(Ord.intOrd, completionStrategy(cs)), is(s.sort(Ord.intOrd))); + } + + @Test + public void testStrategyMergeAll() { + final List l = List.range(0, 100); + final List> p1s = mergeAll(l.map(x -> CompletableFuture.supplyAsync(() -> x))); + assertThat(P1.sequence(p1s)._1(), is(l)); + } + + @Test + public void testStrategyCallables() throws Exception { + final Strategy> s = strategy(c -> c); + final Strategy> cs = callableStrategy(s); + assertThat(callableStrategy(s).par(P.p(callable(1)))._1().call(), is(1)); + } +} diff --git a/core/src/test/java/fj/data/StreamTest.java b/core/src/test/java/fj/data/StreamTest.java index b65e115c..999a3a3e 100644 --- a/core/src/test/java/fj/data/StreamTest.java +++ b/core/src/test/java/fj/data/StreamTest.java @@ -1,9 +1,7 @@ package fj.data; import fj.Equal; -import fj.Ord; import fj.P2; -import fj.control.parallel.Strategy; import org.junit.Test; import java.util.ConcurrentModificationException; @@ -105,18 +103,4 @@ public void testMinus() { assertThat(s1.minus(Equal.charEqual, s2), is(stream(new Character[]{'a', 'c', 'd'}))); } - - @Test - public void testSortSeq() { - Stream s = range(Enumerator.intEnumerator, 99, -99, -1); - assertThat(s.sort(Ord.intOrd, Strategy.seqStrategy()), - is(s.sort(Ord.intOrd))); - } - - @Test - public void testSortThread() { - Stream s = range(Enumerator.intEnumerator, 99, -99, -1); - assertThat(s.sort(Ord.intOrd, Strategy.simpleThreadStrategy()), - is(s.sort(Ord.intOrd))); - } } From 9893314de654075a46f53f6aa6edb4e0426228bd Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 7 Oct 2018 11:13:26 +0200 Subject: [PATCH 054/173] Fix regression in lifted semigroup sum. Move lifted definition in Monoid. --- core/src/main/java/fj/Monoid.java | 31 +++++++++++++++++++++++++++ core/src/main/java/fj/Semigroup.java | 24 +-------------------- core/src/test/java/fj/MonoidTest.java | 19 ++++++++++++++++ 3 files changed, 51 insertions(+), 23 deletions(-) create mode 100644 core/src/test/java/fj/MonoidTest.java diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index 3fcf1775..cdb8e746 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -17,6 +17,7 @@ import static fj.data.List.nil; import static fj.data.Natural.natural; import static fj.data.Option.none; +import static fj.data.Option.some; import static fj.data.Stream.iterableStream; import java.math.BigInteger; @@ -867,6 +868,36 @@ public List sum(F0>> as) { }); } + /** + * Lift a {@code Semigroup} for A to a {@code Monoid>}, using Option.none() as zero. + * + * @return A monoid for option. + */ + public static Monoid> optionMonoid(Semigroup aSemigroup) { + return monoidDef(new Monoid.Definition>() { + @Override + public Option empty() { + return none(); + } + + @Override + public Option append(Option a1, Option a2) { + return a1.liftM2(a2, aSemigroup::sum).orElse(a1).orElse(a2); + } + + @Override + public Option multiply(int n, Option oa) { + return n > 0 ? oa.map(a -> aSemigroup.multiply1p(n - 1, a)) : none(); + } + + @Override + public Option sum(F0>> oas) { + Stream as = oas.f().bind(Option::toStream); + return as.uncons(none(), h -> tail -> some(aSemigroup.sumStream(h, tail::_1))); + } + }); + } + /** * A monoid for options. * @deprecated since 4.7. Use {@link #firstOptionMonoid()}. diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index 16b0d130..f626ded7 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -172,29 +172,7 @@ public Semigroup dual() { * Lifts the semigroup to obtain a trivial monoid. */ public Monoid> lift() { - Definition def = this.def; - return monoidDef(new Monoid.Definition>() { - @Override - public Option empty() { - return none(); - } - - @Override - public Option append(Option a1, Option a2) { - return a1.liftM2(a1, def::append).orElse(a1).orElse(a2); - } - - @Override - public Option multiply(int n, Option oa) { - return n > 0 ? oa.map(a -> def.multiply1p(n - 1, a)) : none(); - } - - @Override - public Option sum(F0>> oas) { - Stream as = oas.f().bind(Option::toStream); - return as.uncons(none(), h -> tail -> some(def.sum(h, tail::_1))); - } - }); + return Monoid.optionMonoid(this); } /** diff --git a/core/src/test/java/fj/MonoidTest.java b/core/src/test/java/fj/MonoidTest.java new file mode 100644 index 00000000..8bab5315 --- /dev/null +++ b/core/src/test/java/fj/MonoidTest.java @@ -0,0 +1,19 @@ +package fj; + +import fj.data.Option; +import fj.data.Stream; +import org.junit.Test; + +import static fj.data.Option.some; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class MonoidTest { + + @Test + public void lifted_sum_of_two_numbers() { + Monoid> optionMonoid = Semigroup.intAdditionSemigroup.lift(); + assertThat(optionMonoid.sum(some(3), some(5)), is(some(8))); + assertThat(optionMonoid.sumLeft(Stream.arrayStream(some(3), some(5))), is(some(8))); + } +} From d3d876c28b31b5ad210302614abdd2e8c7b3165f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 7 Oct 2018 11:25:47 +0200 Subject: [PATCH 055/173] Add Trampoline.suspend(final F0> a). --- core/src/main/java/fj/control/Trampoline.java | 17 ++++++++++++++--- core/src/main/java/fj/data/DList.java | 2 +- core/src/main/java/fj/data/Eval.java | 6 +++--- core/src/main/java/fj/data/List.java | 2 +- core/src/main/java/fj/data/State.java | 3 +-- quickcheck/src/main/java/fj/test/Gen.java | 5 ++--- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/fj/control/Trampoline.java b/core/src/main/java/fj/control/Trampoline.java index 37491c3b..0b6060b1 100644 --- a/core/src/main/java/fj/control/Trampoline.java +++ b/core/src/main/java/fj/control/Trampoline.java @@ -46,7 +46,7 @@ public R fold(final F, R> n, // The monadic bind constructs a new Codense whose subcomputation is still `sub`, and Kleisli-composes the // continuations. public Trampoline bind(final F> f) { - return codense(sub, o -> suspend(P.lazy(() -> cont.f(o).bind(f)))); + return codense(sub, o -> suspend(() -> cont.f(o).bind(f))); } // The resumption of a Codense is the resumption of its subcomputation. If that computation is done, its result @@ -126,6 +126,16 @@ public static Trampoline pure(final A a) { return new Pure<>(a); } + /** + * Suspends the given computation in a thunk. + * + * @param a A trampoline suspended in a thunk. + * @return A trampoline whose next step runs the given thunk. + */ + public static Trampoline suspend(final F0> a) { + return new Suspend<>(P.lazy(a)); + } + /** * Suspends the given computation in a thunk. * @@ -136,6 +146,7 @@ public static Trampoline suspend(final P1> a) { return new Suspend<>(a); } + /** * @return The first-class version of `suspend`. */ @@ -255,7 +266,7 @@ public final Trampoline zipWith(final Trampoline b, final F2>, B> eb = b.resume(); for (final P1> x : ea.left()) { for (final P1> y : eb.left()) { - return suspend(x.bind(y, F2Functions.curry((ta, tb) -> suspend(P.lazy(() -> ta.zipWith(tb, f)))))); + return suspend(x.bind(y, F2Functions.curry((ta, tb) -> suspend(() -> ta.zipWith(tb, f))))); } for (final B y : eb.right()) { return suspend(x.map(ta -> ta.map(F2Functions.f(F2Functions.flip(f), y)))); @@ -263,7 +274,7 @@ public final Trampoline zipWith(final Trampoline b, final F2 pure(f.f(x, y)))); + return suspend(() -> pure(f.f(x, y))); } for (final P1> y : eb.left()) { return suspend(y.map(liftM2(F2Functions.curry(f)).f(pure(x)))); diff --git a/core/src/main/java/fj/data/DList.java b/core/src/main/java/fj/data/DList.java index af9d68b0..597b6dda 100644 --- a/core/src/main/java/fj/data/DList.java +++ b/core/src/main/java/fj/data/DList.java @@ -126,6 +126,6 @@ public DList append(DList other) { } private static F> kleisliTrampCompose(F> bc, F> ab) { - return (A a) -> ab.f(a).bind((B b) -> Trampoline.suspend(P.lazy(() -> bc.f(b)))); + return (A a) -> ab.f(a).bind((B b) -> Trampoline.suspend(() -> bc.f(b))); } } diff --git a/core/src/main/java/fj/data/Eval.java b/core/src/main/java/fj/data/Eval.java index 3e513075..5d09f913 100644 --- a/core/src/main/java/fj/data/Eval.java +++ b/core/src/main/java/fj/data/Eval.java @@ -204,7 +204,7 @@ private static final class PureTrampolineEval extends TrampolineEval { @Override protected final Trampoline trampoline() { - return Trampoline.suspend(P.lazy(() -> Trampoline.pure(start.value()))); + return Trampoline.suspend(() -> Trampoline.pure(start.value())); } } @@ -219,7 +219,7 @@ private static final class BindTrampolineEval extends TrampolineEval { @Override protected final Trampoline trampoline() { - return Trampoline.suspend(P.lazy(() -> next.trampoline().bind(v -> f.f(v).asTrampoline().trampoline()))); + return Trampoline.suspend(() -> next.trampoline().bind(v -> f.f(v).asTrampoline().trampoline())); } } @@ -232,7 +232,7 @@ private static final class DeferEval extends TrampolineEval { @Override protected final Trampoline trampoline() { - return Trampoline.suspend(P.lazy(() -> memo._1().asTrampoline().trampoline())); + return Trampoline.suspend(() -> memo._1().asTrampoline().trampoline()); } } } diff --git a/core/src/main/java/fj/data/List.java b/core/src/main/java/fj/data/List.java index f58fd73f..e0e12877 100644 --- a/core/src/main/java/fj/data/List.java +++ b/core/src/main/java/fj/data/List.java @@ -736,7 +736,7 @@ public final B foldRight(final F2 f, final B b) { * @return A Trampoline containing the final result after the right-fold reduction. */ public final Trampoline foldRightC(final F2 f, final B b) { - return Trampoline.suspend(P.lazy(() -> isEmpty() ? Trampoline.pure(b) : tail().foldRightC(f, b).map(F2Functions.f(f, head())))); + return Trampoline.suspend(() -> isEmpty() ? Trampoline.pure(b) : tail().foldRightC(f, b).map(F2Functions.f(f, head()))); } /** diff --git a/core/src/main/java/fj/data/State.java b/core/src/main/java/fj/data/State.java index 1b30a55e..f29d55d7 100644 --- a/core/src/main/java/fj/data/State.java +++ b/core/src/main/java/fj/data/State.java @@ -5,7 +5,6 @@ import fj.Unit; import fj.control.Trampoline; -import static fj.P.lazy; import static fj.P.p; import static fj.control.Trampoline.suspend; import static fj.data.List.cons; @@ -75,7 +74,7 @@ public static State> traverse(List list, F State suspended(F>> runF) { - return new State<>(s -> suspend(lazy(() -> runF.f(s)))); + return new State<>(s -> suspend(() -> runF.f(s))); } private final F>> runF; diff --git a/quickcheck/src/main/java/fj/test/Gen.java b/quickcheck/src/main/java/fj/test/Gen.java index a5e30a9d..323995e1 100644 --- a/quickcheck/src/main/java/fj/test/Gen.java +++ b/quickcheck/src/main/java/fj/test/Gen.java @@ -16,7 +16,6 @@ import static fj.Function.flip; import static fj.Monoid.intAdditionMonoid; import static fj.Ord.intOrd; -import static fj.P.lazy; import static fj.P2.__1; import static fj.control.Trampoline.pure; import static fj.control.Trampoline.suspend; @@ -551,7 +550,7 @@ final class Tramp { // Picks elements in constant stack space private Trampoline> tramp(List remainAs, int remainN, int remainALength) { - return suspend(lazy(() -> + return suspend(() -> (remainN == 0) ? // We have picked N elements; stop pure(nil()) : @@ -559,7 +558,7 @@ private Trampoline> tramp(List remainAs, int remainN, int remainALeng (r.choose(0, remainALength - 1) < remainN) ? tramp(remainAs.tail(), remainN - 1, remainALength - 1) .map(pickedTail -> cons(remainAs.head(), pickedTail)) : - tramp(remainAs.tail(), remainN, remainALength - 1))); + tramp(remainAs.tail(), remainN, remainALength - 1)); } } From cf1188acafd62d60f1736a0d945790771029079b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 7 Oct 2018 11:13:26 +0200 Subject: [PATCH 056/173] Fix regression in lifted semigroup sum. Move lifted definition in Monoid. --- core/src/main/java/fj/Monoid.java | 31 +++++++++++++++++++++++++++ core/src/main/java/fj/Semigroup.java | 24 +-------------------- core/src/test/java/fj/MonoidTest.java | 19 ++++++++++++++++ 3 files changed, 51 insertions(+), 23 deletions(-) create mode 100644 core/src/test/java/fj/MonoidTest.java diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index 3fcf1775..cdb8e746 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -17,6 +17,7 @@ import static fj.data.List.nil; import static fj.data.Natural.natural; import static fj.data.Option.none; +import static fj.data.Option.some; import static fj.data.Stream.iterableStream; import java.math.BigInteger; @@ -867,6 +868,36 @@ public List sum(F0>> as) { }); } + /** + * Lift a {@code Semigroup} for A to a {@code Monoid>}, using Option.none() as zero. + * + * @return A monoid for option. + */ + public static Monoid> optionMonoid(Semigroup aSemigroup) { + return monoidDef(new Monoid.Definition>() { + @Override + public Option empty() { + return none(); + } + + @Override + public Option append(Option a1, Option a2) { + return a1.liftM2(a2, aSemigroup::sum).orElse(a1).orElse(a2); + } + + @Override + public Option multiply(int n, Option oa) { + return n > 0 ? oa.map(a -> aSemigroup.multiply1p(n - 1, a)) : none(); + } + + @Override + public Option sum(F0>> oas) { + Stream as = oas.f().bind(Option::toStream); + return as.uncons(none(), h -> tail -> some(aSemigroup.sumStream(h, tail::_1))); + } + }); + } + /** * A monoid for options. * @deprecated since 4.7. Use {@link #firstOptionMonoid()}. diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index 16b0d130..f626ded7 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -172,29 +172,7 @@ public Semigroup dual() { * Lifts the semigroup to obtain a trivial monoid. */ public Monoid> lift() { - Definition def = this.def; - return monoidDef(new Monoid.Definition>() { - @Override - public Option empty() { - return none(); - } - - @Override - public Option append(Option a1, Option a2) { - return a1.liftM2(a1, def::append).orElse(a1).orElse(a2); - } - - @Override - public Option multiply(int n, Option oa) { - return n > 0 ? oa.map(a -> def.multiply1p(n - 1, a)) : none(); - } - - @Override - public Option sum(F0>> oas) { - Stream as = oas.f().bind(Option::toStream); - return as.uncons(none(), h -> tail -> some(def.sum(h, tail::_1))); - } - }); + return Monoid.optionMonoid(this); } /** diff --git a/core/src/test/java/fj/MonoidTest.java b/core/src/test/java/fj/MonoidTest.java new file mode 100644 index 00000000..8bab5315 --- /dev/null +++ b/core/src/test/java/fj/MonoidTest.java @@ -0,0 +1,19 @@ +package fj; + +import fj.data.Option; +import fj.data.Stream; +import org.junit.Test; + +import static fj.data.Option.some; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class MonoidTest { + + @Test + public void lifted_sum_of_two_numbers() { + Monoid> optionMonoid = Semigroup.intAdditionSemigroup.lift(); + assertThat(optionMonoid.sum(some(3), some(5)), is(some(8))); + assertThat(optionMonoid.sumLeft(Stream.arrayStream(some(3), some(5))), is(some(8))); + } +} From c3ea5d36dd7869e8e4da411b4d9b926a44fb4bcd Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 7 Oct 2018 11:25:47 +0200 Subject: [PATCH 057/173] Add Trampoline.suspend(final F0> a). --- core/src/main/java/fj/control/Trampoline.java | 17 ++++++++++++++--- core/src/main/java/fj/data/DList.java | 2 +- core/src/main/java/fj/data/Eval.java | 6 +++--- core/src/main/java/fj/data/List.java | 2 +- core/src/main/java/fj/data/State.java | 3 +-- quickcheck/src/main/java/fj/test/Gen.java | 5 ++--- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/fj/control/Trampoline.java b/core/src/main/java/fj/control/Trampoline.java index 37491c3b..0b6060b1 100644 --- a/core/src/main/java/fj/control/Trampoline.java +++ b/core/src/main/java/fj/control/Trampoline.java @@ -46,7 +46,7 @@ public R fold(final F, R> n, // The monadic bind constructs a new Codense whose subcomputation is still `sub`, and Kleisli-composes the // continuations. public Trampoline bind(final F> f) { - return codense(sub, o -> suspend(P.lazy(() -> cont.f(o).bind(f)))); + return codense(sub, o -> suspend(() -> cont.f(o).bind(f))); } // The resumption of a Codense is the resumption of its subcomputation. If that computation is done, its result @@ -126,6 +126,16 @@ public static Trampoline pure(final A a) { return new Pure<>(a); } + /** + * Suspends the given computation in a thunk. + * + * @param a A trampoline suspended in a thunk. + * @return A trampoline whose next step runs the given thunk. + */ + public static Trampoline suspend(final F0> a) { + return new Suspend<>(P.lazy(a)); + } + /** * Suspends the given computation in a thunk. * @@ -136,6 +146,7 @@ public static Trampoline suspend(final P1> a) { return new Suspend<>(a); } + /** * @return The first-class version of `suspend`. */ @@ -255,7 +266,7 @@ public final Trampoline zipWith(final Trampoline b, final F2>, B> eb = b.resume(); for (final P1> x : ea.left()) { for (final P1> y : eb.left()) { - return suspend(x.bind(y, F2Functions.curry((ta, tb) -> suspend(P.lazy(() -> ta.zipWith(tb, f)))))); + return suspend(x.bind(y, F2Functions.curry((ta, tb) -> suspend(() -> ta.zipWith(tb, f))))); } for (final B y : eb.right()) { return suspend(x.map(ta -> ta.map(F2Functions.f(F2Functions.flip(f), y)))); @@ -263,7 +274,7 @@ public final Trampoline zipWith(final Trampoline b, final F2 pure(f.f(x, y)))); + return suspend(() -> pure(f.f(x, y))); } for (final P1> y : eb.left()) { return suspend(y.map(liftM2(F2Functions.curry(f)).f(pure(x)))); diff --git a/core/src/main/java/fj/data/DList.java b/core/src/main/java/fj/data/DList.java index af9d68b0..597b6dda 100644 --- a/core/src/main/java/fj/data/DList.java +++ b/core/src/main/java/fj/data/DList.java @@ -126,6 +126,6 @@ public DList append(DList other) { } private static F> kleisliTrampCompose(F> bc, F> ab) { - return (A a) -> ab.f(a).bind((B b) -> Trampoline.suspend(P.lazy(() -> bc.f(b)))); + return (A a) -> ab.f(a).bind((B b) -> Trampoline.suspend(() -> bc.f(b))); } } diff --git a/core/src/main/java/fj/data/Eval.java b/core/src/main/java/fj/data/Eval.java index 3e513075..5d09f913 100644 --- a/core/src/main/java/fj/data/Eval.java +++ b/core/src/main/java/fj/data/Eval.java @@ -204,7 +204,7 @@ private static final class PureTrampolineEval extends TrampolineEval { @Override protected final Trampoline trampoline() { - return Trampoline.suspend(P.lazy(() -> Trampoline.pure(start.value()))); + return Trampoline.suspend(() -> Trampoline.pure(start.value())); } } @@ -219,7 +219,7 @@ private static final class BindTrampolineEval extends TrampolineEval { @Override protected final Trampoline trampoline() { - return Trampoline.suspend(P.lazy(() -> next.trampoline().bind(v -> f.f(v).asTrampoline().trampoline()))); + return Trampoline.suspend(() -> next.trampoline().bind(v -> f.f(v).asTrampoline().trampoline())); } } @@ -232,7 +232,7 @@ private static final class DeferEval extends TrampolineEval { @Override protected final Trampoline trampoline() { - return Trampoline.suspend(P.lazy(() -> memo._1().asTrampoline().trampoline())); + return Trampoline.suspend(() -> memo._1().asTrampoline().trampoline()); } } } diff --git a/core/src/main/java/fj/data/List.java b/core/src/main/java/fj/data/List.java index f58fd73f..e0e12877 100644 --- a/core/src/main/java/fj/data/List.java +++ b/core/src/main/java/fj/data/List.java @@ -736,7 +736,7 @@ public final B foldRight(final F2 f, final B b) { * @return A Trampoline containing the final result after the right-fold reduction. */ public final Trampoline foldRightC(final F2 f, final B b) { - return Trampoline.suspend(P.lazy(() -> isEmpty() ? Trampoline.pure(b) : tail().foldRightC(f, b).map(F2Functions.f(f, head())))); + return Trampoline.suspend(() -> isEmpty() ? Trampoline.pure(b) : tail().foldRightC(f, b).map(F2Functions.f(f, head()))); } /** diff --git a/core/src/main/java/fj/data/State.java b/core/src/main/java/fj/data/State.java index 1b30a55e..f29d55d7 100644 --- a/core/src/main/java/fj/data/State.java +++ b/core/src/main/java/fj/data/State.java @@ -5,7 +5,6 @@ import fj.Unit; import fj.control.Trampoline; -import static fj.P.lazy; import static fj.P.p; import static fj.control.Trampoline.suspend; import static fj.data.List.cons; @@ -75,7 +74,7 @@ public static State> traverse(List list, F State suspended(F>> runF) { - return new State<>(s -> suspend(lazy(() -> runF.f(s)))); + return new State<>(s -> suspend(() -> runF.f(s))); } private final F>> runF; diff --git a/quickcheck/src/main/java/fj/test/Gen.java b/quickcheck/src/main/java/fj/test/Gen.java index a5e30a9d..323995e1 100644 --- a/quickcheck/src/main/java/fj/test/Gen.java +++ b/quickcheck/src/main/java/fj/test/Gen.java @@ -16,7 +16,6 @@ import static fj.Function.flip; import static fj.Monoid.intAdditionMonoid; import static fj.Ord.intOrd; -import static fj.P.lazy; import static fj.P2.__1; import static fj.control.Trampoline.pure; import static fj.control.Trampoline.suspend; @@ -551,7 +550,7 @@ final class Tramp { // Picks elements in constant stack space private Trampoline> tramp(List remainAs, int remainN, int remainALength) { - return suspend(lazy(() -> + return suspend(() -> (remainN == 0) ? // We have picked N elements; stop pure(nil()) : @@ -559,7 +558,7 @@ private Trampoline> tramp(List remainAs, int remainN, int remainALeng (r.choose(0, remainALength - 1) < remainN) ? tramp(remainAs.tail(), remainN - 1, remainALength - 1) .map(pickedTail -> cons(remainAs.head(), pickedTail)) : - tramp(remainAs.tail(), remainN, remainALength - 1))); + tramp(remainAs.tail(), remainN, remainALength - 1)); } } From e1a0983a365ca6a934dccf14a181a9ba486ccdd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sun, 23 Sep 2018 20:28:40 +0200 Subject: [PATCH 058/173] Add Strategy tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták (cherry picked from commit 2e1f2c57176a2bf8974ef0725e681e9042cff9ec) --- .../fj/control/parallel/StrategyTest.java | 61 +++++++++++++++++++ core/src/test/java/fj/data/StreamTest.java | 16 ----- 2 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 core/src/test/java/fj/control/parallel/StrategyTest.java diff --git a/core/src/test/java/fj/control/parallel/StrategyTest.java b/core/src/test/java/fj/control/parallel/StrategyTest.java new file mode 100644 index 00000000..ee3b30df --- /dev/null +++ b/core/src/test/java/fj/control/parallel/StrategyTest.java @@ -0,0 +1,61 @@ +package fj.control.parallel; + +import fj.Ord; +import fj.P; +import fj.P1; +import fj.data.Enumerator; +import fj.data.List; +import fj.data.Stream; +import org.junit.Test; + +import java.util.concurrent.*; + +import static fj.control.parallel.Callables.callable; +import static fj.control.parallel.Strategy.*; +import static fj.data.Stream.range; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class StrategyTest { + + @Test + public void testStrategySeq() { + final Stream s = range(Enumerator.intEnumerator, 99, -99, -1); + assertThat(s.sort(Ord.intOrd, seqStrategy()), is(s.sort(Ord.intOrd))); + } + + @Test + public void testStrategyThread() { + final Stream s = range(Enumerator.intEnumerator, 99, -99, -1); + assertThat(s.sort(Ord.intOrd, simpleThreadStrategy()), is(s.sort(Ord.intOrd))); + } + + @Test + public void testStrategyExecutor() { + final Stream s = range(Enumerator.intEnumerator, 99, -99, -1); + final ExecutorService es = Executors.newFixedThreadPool(10); + assertThat(s.sort(Ord.intOrd, executorStrategy(es)), is(s.sort(Ord.intOrd))); + } + + @Test + public void testStrategyCompletion() { + final Stream s = range(Enumerator.intEnumerator, 99, -99, -1); + final ExecutorService es = Executors.newFixedThreadPool(10); + final CompletionService cs = new ExecutorCompletionService(es); + assertThat(s.sort(Ord.intOrd, completionStrategy(cs)), is(s.sort(Ord.intOrd))); + } + + @Test + public void testStrategyMergeAll() { + final List l = List.range(0, 100); + final List> p1s = mergeAll(l.map(x -> CompletableFuture.supplyAsync(() -> x))); + assertThat(P1.sequence(p1s)._1(), is(l)); + } + + @Test + public void testStrategyCallables() throws Exception { + final Strategy> s = strategy(c -> c); + final Strategy> cs = callableStrategy(s); + assertThat(callableStrategy(s).par(P.p(callable(1)))._1().call(), is(1)); + } +} diff --git a/core/src/test/java/fj/data/StreamTest.java b/core/src/test/java/fj/data/StreamTest.java index b65e115c..999a3a3e 100644 --- a/core/src/test/java/fj/data/StreamTest.java +++ b/core/src/test/java/fj/data/StreamTest.java @@ -1,9 +1,7 @@ package fj.data; import fj.Equal; -import fj.Ord; import fj.P2; -import fj.control.parallel.Strategy; import org.junit.Test; import java.util.ConcurrentModificationException; @@ -105,18 +103,4 @@ public void testMinus() { assertThat(s1.minus(Equal.charEqual, s2), is(stream(new Character[]{'a', 'c', 'd'}))); } - - @Test - public void testSortSeq() { - Stream s = range(Enumerator.intEnumerator, 99, -99, -1); - assertThat(s.sort(Ord.intOrd, Strategy.seqStrategy()), - is(s.sort(Ord.intOrd))); - } - - @Test - public void testSortThread() { - Stream s = range(Enumerator.intEnumerator, 99, -99, -1); - assertThat(s.sort(Ord.intOrd, Strategy.simpleThreadStrategy()), - is(s.sort(Ord.intOrd))); - } } From 7b3cdae5e9d708904d72afffe039710ed8a9ad28 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 7 Oct 2018 20:00:44 +0200 Subject: [PATCH 059/173] Add missing @Deprecated annotations (previously only on javadoc). --- core/src/main/java/fj/P1.java | 3 ++- core/src/main/java/fj/Semigroup.java | 3 ++- core/src/main/java/fj/data/Java.java | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/fj/P1.java b/core/src/main/java/fj/P1.java index 555a28ae..5e627403 100644 --- a/core/src/main/java/fj/P1.java +++ b/core/src/main/java/fj/P1.java @@ -42,10 +42,11 @@ public static F, A> __1() { /** * Promote any function to a transformation between P1s. * - * @deprecated As of release 4.5, use {@link #map_} + * @deprecated As of release 4.5, use {@link #map_} * @param f A function to promote to a transformation between P1s. * @return A function promoted to operate on P1s. */ + @Deprecated public static F, P1> fmap(final F f) { return map_(f); } diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index f626ded7..075dfeda 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -527,8 +527,9 @@ public NonEmptyList sum(NonEmptyList nea, F0>> neas * A semigroup for optional values. * @deprecated since 4.7. Use {@link #firstOptionSemigroup()}. * - ** @return A semigroup for optional values. + * @return A semigroup for optional values. */ + @Deprecated public static Semigroup> optionSemigroup() { return firstOptionSemigroup(); } diff --git a/core/src/main/java/fj/data/Java.java b/core/src/main/java/fj/data/Java.java index 80a5ec43..ae7fcf6a 100644 --- a/core/src/main/java/fj/data/Java.java +++ b/core/src/main/java/fj/data/Java.java @@ -1420,6 +1420,7 @@ public static F, List> ArrayList_List() { * * @return A function that converts Java lists to lists. */ + @Deprecated public static F, List> JUList_List() { return Java::JavaList_List; } From 9f489c9428ff06ea39400239faa929eae9d78bba Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 7 Oct 2018 21:08:42 +0200 Subject: [PATCH 060/173] Fix compile warnings. --- core/src/test/java/fj/FWFunctionsTest.java | 4 ++-- core/src/test/java/fj/TryEffectTest.java | 6 ++---- core/src/test/java/fj/control/parallel/StrategyTest.java | 3 ++- props-core/src/test/java/fj/MemoisationTest.java | 9 +++++---- .../test/java/fj/data/fingertrees/FingerTreeTest.java | 6 ++++-- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/core/src/test/java/fj/FWFunctionsTest.java b/core/src/test/java/fj/FWFunctionsTest.java index c1f69718..1c034ab9 100644 --- a/core/src/test/java/fj/FWFunctionsTest.java +++ b/core/src/test/java/fj/FWFunctionsTest.java @@ -9,14 +9,14 @@ public class FWFunctionsTest { @Test public void testLift1() { F f = i -> i + 1; - F1W f1w = F1W.lift(f); + F1W f1w = F1W.lift(f); assertThat(f1w.f(1), is(2)); } @Test public void testLift2() { F2 f2 = (i, j) -> i + j; - F2W f2w = F2W.lift(f2); + F2W f2w = F2W.lift(f2); assertThat(f2w.f(1, 2), is(3)); } diff --git a/core/src/test/java/fj/TryEffectTest.java b/core/src/test/java/fj/TryEffectTest.java index bb684fc6..ea92b8f2 100644 --- a/core/src/test/java/fj/TryEffectTest.java +++ b/core/src/test/java/fj/TryEffectTest.java @@ -11,8 +11,7 @@ public class TryEffectTest { @Test public void testTryEffect0Success() { - F> f = - TryEffect.f(TryEffect0::f); + F, Validation> f = TryEffect.f(TryEffect0::f); Validation v = f.f(new AlwaysSucceed0()); assertThat(v.isSuccess(), is(true)); assertThat(v.success(), is(Unit.unit())); @@ -20,8 +19,7 @@ public void testTryEffect0Success() { @Test public void testTryEffect0Fail() { - F> f = - TryEffect.f(TryEffect0::f); + F, Validation> f = TryEffect.f(TryEffect0::f); Validation v = f.f(new AlwaysFail0()); assertThat(v.isFail(), is(true)); assertThat(v.fail(), is(new TryEffectException())); diff --git a/core/src/test/java/fj/control/parallel/StrategyTest.java b/core/src/test/java/fj/control/parallel/StrategyTest.java index ee3b30df..8475b35a 100644 --- a/core/src/test/java/fj/control/parallel/StrategyTest.java +++ b/core/src/test/java/fj/control/parallel/StrategyTest.java @@ -3,6 +3,7 @@ import fj.Ord; import fj.P; import fj.P1; +import fj.Unit; import fj.data.Enumerator; import fj.data.List; import fj.data.Stream; @@ -41,7 +42,7 @@ public void testStrategyExecutor() { public void testStrategyCompletion() { final Stream s = range(Enumerator.intEnumerator, 99, -99, -1); final ExecutorService es = Executors.newFixedThreadPool(10); - final CompletionService cs = new ExecutorCompletionService(es); + final CompletionService cs = new ExecutorCompletionService<>(es); assertThat(s.sort(Ord.intOrd, completionStrategy(cs)), is(s.sort(Ord.intOrd))); } diff --git a/props-core/src/test/java/fj/MemoisationTest.java b/props-core/src/test/java/fj/MemoisationTest.java index 65fa83fc..865f76a3 100644 --- a/props-core/src/test/java/fj/MemoisationTest.java +++ b/props-core/src/test/java/fj/MemoisationTest.java @@ -6,6 +6,7 @@ import org.junit.runner.RunWith; import static fj.test.Arbitrary.arbInteger; +import static fj.test.Arbitrary.arbString; import static fj.test.CheckResult.summary; import static fj.test.Property.prop; import static fj.test.Property.property; @@ -25,16 +26,16 @@ public Property test1() { } public Property test1_hardMemo() { - return property(arbInteger, a -> { - P1 t = P.hardMemo(() -> new Integer(a)); + return property(arbString, a -> { + P1 t = P.hardMemo(() -> new String(a)); return prop(t._1() == t._1()).and(prop(t._1().equals(a))); }); } @Test public Property test2() { - return property(arbInteger, arbInteger, (a, b) -> { - P2 t = P.lazy(u -> new Integer(a), u -> new Integer(b)).memo(); + return property(arbString, arbString, (a, b) -> { + P2 t = P.lazy(u -> new String(a), u -> new String(b)).memo(); return prop(t._1().equals(t._1()) && t._1().equals(a) && t._2().equals(t._2()) && t._2().equals(b) ); }); } diff --git a/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java b/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java index 57e1fae6..b25fff51 100644 --- a/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java +++ b/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java @@ -42,14 +42,16 @@ void validateOperations(List list) { @Test public void testHeadOption() { assertThat(Empty.emptyIntAddition().headOption(), is(Option.none())); - FingerTree ft = new MakeTree(measured(intAdditionMonoid, Function.constant(1))).single(1); + FingerTree ft = new MakeTree(measured(intAdditionMonoid, Function.constant(1))) + .single(1); assertThat(ft.headOption(), is(Option.some(1))); } @Test public void testUncons() { assertThat(Empty.emptyIntAddition().uncons(0, (h, t) -> h), is(0)); - FingerTree ft = new MakeTree(measured(intAdditionMonoid, Function.constant(1))).single(1); + FingerTree ft = new MakeTree(measured(intAdditionMonoid, Function.constant(1))) + .single(1); assertThat(ft.uncons(0, (h, t) -> h), is(1)); } From 4c93bae8c23385b9d9acc0e2358073384a10ae03 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 7 Oct 2018 21:09:17 +0200 Subject: [PATCH 061/173] Update gradle. Fix gradle warnings. --- build.gradle | 24 ++++++++--------------- gradle/wrapper/gradle-wrapper.jar | Bin 54708 -> 56177 bytes gradle/wrapper/gradle-wrapper.properties | 3 +-- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index 9d98d2b3..bc292a82 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,11 @@ buildscript { classpath "com.ofg:uptodate-gradle-plugin:$uptodateVersion" classpath "me.tatarka:gradle-retrolambda:$retrolambdaPluginVersion" } + + wrapper { + gradleVersion = "4.10.2" + distributionType = Wrapper.DistributionType.ALL + } } if (JavaVersion.current().isJava8Compatible()) { @@ -117,23 +122,10 @@ subprojects { } } - jacocoTestReport { - additionalSourceDirs = files(sourceSets.main.allSource.srcDirs) - sourceDirectories = files(sourceSets.main.allSource.srcDirs) - classDirectories = files(sourceSets.main.output) - reports { - html.enabled = true - xml.enabled = true - csv.enabled = false - } - } - - task coverage(dependsOn: ["test", "jacocoTestReport"]) << {} - } task coverage(type: org.gradle.testing.jacoco.tasks.JacocoReport) { - dependsOn = subprojects.coverage + dependsOn = subprojects*.test executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec") // We only care about coverage of: def projectForFoverage = ["core", "java8", "quickcheck", "java-core"] @@ -192,7 +184,7 @@ configure(subprojects.findAll { it.name != "props-core" }) { } // Output MANIFEST.MF statically so eclipse can see it for plugin development - task eclipsePluginManifest(dependsOn: jar) << { + task eclipsePluginManifest(dependsOn: jar) doLast { file("META-INF").mkdirs() jar.manifest.writeTo(file("META-INF/MANIFEST.MF")) } @@ -200,6 +192,6 @@ configure(subprojects.findAll { it.name != "props-core" }) { eclipseProject.dependsOn eclipsePluginManifest } -task env << { +task env doLast { println System.getenv() } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7a3265ee94c0ab25cf079ac8ccdf87f41d455d42..29953ea141f55e3b8fc691d31b5ca8816d89fa87 100644 GIT binary patch delta 50157 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 *SW$u63w9|gi(C$5HMvX&ZbF|8 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 HXWjn;W&S@( delta 48671 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 p-JqCdbEt2u3x<%@f8MG(Y}9d{||T4^I-r0 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 72c40576..d76b502e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sat Aug 25 14:20:05 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip From b797968ae46d5eafd951b03c68d649b9aae8f654 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 7 Oct 2018 21:52:42 +0200 Subject: [PATCH 062/173] Fix javadoc warnings. --- core/src/main/java/fj/Function.java | 28 +++++++++---------- core/src/main/java/fj/Monoid.java | 2 +- core/src/main/java/fj/Semigroup.java | 2 +- core/src/main/java/fj/control/Trampoline.java | 12 ++++---- core/src/main/java/fj/data/Enumerator.java | 16 +++++------ core/src/main/java/fj/data/Tree.java | 4 ++- core/src/main/java/fj/data/hlist/HPre.java | 4 +-- core/src/main/java/fj/data/optic/Iso.java | 4 +-- core/src/main/java/fj/data/optic/PIso.java | 16 +++++------ core/src/main/java/fj/data/optic/PLens.java | 26 +++++++++-------- .../main/java/fj/data/optic/POptional.java | 18 ++++++------ core/src/main/java/fj/data/optic/PPrism.java | 17 ++++++----- core/src/main/java/fj/data/optic/PSetter.java | 14 ++++++---- .../main/java/fj/data/optic/PTraversal.java | 6 ++-- quickcheck/src/main/java/fj/test/Gen.java | 12 ++++---- 15 files changed, 96 insertions(+), 85 deletions(-) diff --git a/core/src/main/java/fj/Function.java b/core/src/main/java/fj/Function.java index f95b1c87..995c179d 100644 --- a/core/src/main/java/fj/Function.java +++ b/core/src/main/java/fj/Function.java @@ -778,11 +778,11 @@ public static F join(final F> f) { /** * Partial application of the second argument to the supplied function to get a function of type - * A -> C. Same as flip(f).f(b). + * {@code A -> C}. Same as {@code flip(f).f(b)}. * * @param f The function to partially apply. * @param b The value to apply to the function. - * @return A new function based on f with its second argument applied. + * @return A new function based on {@code f} with its second argument applied. */ public static F partialApply2(final F> f, final B b) { return a -> uncurryF2(f).f(a, b); @@ -790,11 +790,11 @@ public static F partialApply2(final F> f, final B b) /** * Partial application of the third argument to the supplied function to get a function of type - * A -> B -> D. + * {@code A -> B -> D}. * * @param f The function to partially apply. * @param c The value to apply to the function. - * @return A new function based on f with its third argument applied. + * @return A new function based on {@code f} with its third argument applied. */ public static F> partialApply3(final F>> f, final C c) { return a -> b -> uncurryF3(f).f(a, b, c); @@ -802,11 +802,11 @@ public static F> partialApply3(final F>> /** * Partial application of the fourth argument to the supplied function to get a function of type - * A -> B -> C -> E. + * {@code A -> B -> C -> E}. * * @param f The function to partially apply. * @param d The value to apply to the function. - * @return A new function based on f with its fourth argument applied. + * @return A new function based on {@code f} with its fourth argument applied. */ public static F>> partialApply4(final F>>> f, final D d) { return a -> b -> c -> uncurryF4(f).f(a, b, c, d); @@ -814,11 +814,11 @@ public static F>> partialApply4(final FA -> B -> C -> D -> F$. + * {@code A -> B -> C -> D -> F$}. * * @param f The function to partially apply. * @param e The value to apply to the function. - * @return A new function based on f with its fifth argument applied. + * @return A new function based on {@code f} with its fifth argument applied. */ public static F>>> partialApply5(final F>>>> f, final E e) { @@ -827,11 +827,11 @@ public static F>> partialApply4(final FA -> B -> C -> D -> E -> G. + * {@code A -> B -> C -> D -> E -> G}. * * @param f The function to partially apply. * @param f$ The value to apply to the function. - * @return A new function based on f with its sixth argument applied. + * @return A new function based on {@code f} with its sixth argument applied. */ public static F>>>> partialApply6( final F>>>>> f, final F$ f$) { @@ -840,11 +840,11 @@ public static F>> partialApply4(final FA -> B -> C -> D -> E -> F$ -> H. + * {@code A -> B -> C -> D -> E -> F$ -> H}. * * @param f The function to partially apply. * @param g The value to apply to the function. - * @return A new function based on f with its seventh argument applied. + * @return A new function based on {@code f} with its seventh argument applied. */ public static F>>>>> partialApply7( final F>>>>>> f, final G g) { @@ -853,11 +853,11 @@ public static F>> partialApply4(final FA -> B -> C -> D -> E -> F$ -> G -> I. + * {@code A -> B -> C -> D -> E -> F$ -> G -> I}. * * @param f The function to partially apply. * @param h The value to apply to the function. - * @return A new function based on f with its eigth argument applied. + * @return A new function based on {@code f} with its eigth argument applied. */ public static F>>>>>> partialApply8( final F>>>>>>> f, final H h) { diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index cdb8e746..0c09b558 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -246,7 +246,7 @@ public A zero() { /** * Returns a value summed n times (a + a + ... + a). * The default definition uses peasant multiplication, exploiting - * associativity to only require `O(log n)` uses of + * associativity to only require {@code O(log n)} uses of * {@link #sum(Object, Object)}. * * @param n multiplier diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index 075dfeda..42f05f5b 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -134,7 +134,7 @@ public F> sum() { /** * Returns a value summed n + 1 times ( * a + a + ... + a) The default definition uses peasant - * multiplication, exploiting associativity to only require `O(log n)` uses of + * multiplication, exploiting associativity to only require {@code O(log n)} uses of * {@link #sum(Object, Object)}. * * @param n multiplier diff --git a/core/src/main/java/fj/control/Trampoline.java b/core/src/main/java/fj/control/Trampoline.java index 0b6060b1..47fd282b 100644 --- a/core/src/main/java/fj/control/Trampoline.java +++ b/core/src/main/java/fj/control/Trampoline.java @@ -43,7 +43,7 @@ public R fold(final F, R> n, return gs.f(this); } - // The monadic bind constructs a new Codense whose subcomputation is still `sub`, and Kleisli-composes the + // The monadic bind constructs a new Codense whose subcomputation is still {@code sub}, and Kleisli-composes the // continuations. public Trampoline bind(final F> f) { return codense(sub, o -> suspend(() -> cont.f(o).bind(f))); @@ -110,7 +110,7 @@ private static Codense codense(final Normal a, final F F> pure() { return Trampoline::pure; @@ -148,7 +148,7 @@ public static Trampoline suspend(final P1> a) { /** - * @return The first-class version of `suspend`. + * @return The first-class version of {@code suspend}. */ public static F>, Trampoline> suspend_() { return Trampoline::suspend; @@ -175,21 +175,21 @@ public final Trampoline map(final F f) { } /** - * @return The first-class version of `bind`. + * @return The first-class version of {@code bind}. */ public static F>, F, Trampoline>> bind_() { return f -> a -> a.bind(f); } /** - * @return The first-class version of `map`. + * @return The first-class version of {@code map}. */ public static F, F, Trampoline>> map_() { return f -> a -> a.map(f); } /** - * @return The first-class version of `resume`. + * @return The first-class version of {@code resume}. */ public static F, Either>, A>> resume_() { return Trampoline::resume; diff --git a/core/src/main/java/fj/data/Enumerator.java b/core/src/main/java/fj/data/Enumerator.java index 1666d7b2..94f1b2cd 100644 --- a/core/src/main/java/fj/data/Enumerator.java +++ b/core/src/main/java/fj/data/Enumerator.java @@ -18,18 +18,18 @@ /** * Abstracts over a type that may have a successor and/or predecessor value. This implies ordering for that type. A user - * may construct an enumerator with an optimised version for plus, otherwise a default is implemented using + * may construct an enumerator with an optimised version for {@code plus}, otherwise a default is implemented using * the given successor/predecessor implementations. *

      * For any enumerator e, the following laws must satisfy: *

        - *
      • forall a. e.successor(a).forall(\t -> e.predecessor(t).forall(\z -> z == a))
      • - *
      • forall a. e.predecessor(a).forall(\t -> e.successor(t).forall(\z -> z == a))
      • - *
      • e.max().forall(\t -> e.successor(t).isNone)
      • - *
      • e.min().forall(\t -> e.predecessor(t).isNone)
      • - *
      • forall a n. e.plus(a, 0) == Some(a)
      • - *
      • forall a n | n > 0. e.plus(a, n) == e.plus(a, n - 1)
      • - *
      • forall a n | n < 0. e.plus(a, n) == e.plus(a, n + 1)
      • + *
      • {@code forall a. e.successor(a).forall(\t -> e.predecessor(t).forall(\z -> z == a))}
      • + *
      • {@code forall a. e.predecessor(a).forall(\t -> e.successor(t).forall(\z -> z == a))}
      • + *
      • {@code e.max().forall(\t -> e.successor(t).isNone)}
      • + *
      • {@code e.min().forall(\t -> e.predecessor(t).isNone)}
      • + *
      • {@code forall a n. e.plus(a, 0) == Some(a)}
      • + *
      • {@code forall a n | n > 0. e.plus(a, n) == e.plus(a, n - 1)}
      • + *
      • {@code forall a n | n < 0. e.plus(a, n) == e.plus(a, n + 1)}
      • *
      * * @version %build.number% diff --git a/core/src/main/java/fj/data/Tree.java b/core/src/main/java/fj/data/Tree.java index 4b0ce7eb..9eebb932 100644 --- a/core/src/main/java/fj/data/Tree.java +++ b/core/src/main/java/fj/data/Tree.java @@ -134,8 +134,10 @@ public Stream
      f(final Tree t, final P1> xs) { } /** + *
      {@code
          * flatten :: Tree a -> [a]
          * flatten t = squish t []
      +   * }
      * where squish (Node x ts) xs = x:Prelude.foldr squish xs ts * Puts the elements of the tree into a Stream, in pre-order. * @@ -346,4 +348,4 @@ public int length() { return 1 + subForest._1().map(Tree::length).foldLeft((acc, i) -> acc + i, 0); } -} \ No newline at end of file +} diff --git a/core/src/main/java/fj/data/hlist/HPre.java b/core/src/main/java/fj/data/hlist/HPre.java index 6a2556de..6c6174a4 100644 --- a/core/src/main/java/fj/data/hlist/HPre.java +++ b/core/src/main/java/fj/data/hlist/HPre.java @@ -57,7 +57,7 @@ public static HFalse hFalse() { } /** - * Type-level boolean conjunction. A value of this type represents evidence that AB -> C + * Type-level boolean conjunction. A value of this type represents evidence that {@code AB -> C} * * @param
      A boolean * @param A boolean @@ -92,7 +92,7 @@ public static HAnd hAnd(final HTrue a, final HTrue b) { } /** - * Type-level boolean disjunction. A value of this type represents evidence that A+B -> C + * Type-level boolean disjunction. A value of this type represents evidence that {@code A+B -> C} * * @param A boolean * @param A boolean diff --git a/core/src/main/java/fj/data/optic/Iso.java b/core/src/main/java/fj/data/optic/Iso.java index ea9babac..4b41b1ad 100644 --- a/core/src/main/java/fj/data/optic/Iso.java +++ b/core/src/main/java/fj/data/optic/Iso.java @@ -119,10 +119,10 @@ public static Iso iso(final F get, final F reverseGet) * create an {@link Iso} between any type and itself. id is the zero element of optics composition, for all optics o of type O * (e.g. Lens, Iso, Prism, ...): * - *
      +   * 
      {@code
          *  o composeIso Iso.id == o
          *  Iso.id composeO o == o
      -   * 
      + * }
      * * (replace composeO by composeLens, composeIso, composePrism, ...) */ diff --git a/core/src/main/java/fj/data/optic/PIso.java b/core/src/main/java/fj/data/optic/PIso.java index e10b47bb..b97b416f 100644 --- a/core/src/main/java/fj/data/optic/PIso.java +++ b/core/src/main/java/fj/data/optic/PIso.java @@ -21,18 +21,18 @@ /** * A {@link PIso} defines an isomorphism between types S, A and B, T: * - *
      + * 
      {@code
        *              get                           reverse.get
        *     -------------------->             -------------------->
        *   S                       A         T                       B
        *     <--------------------             <--------------------
        *       reverse.reverseGet                   reverseGet
      - * 
      + * }
      * - * In addition, if f and g forms an isomorphism between `A` and `B`, i.e. if `f . g = id` and `g . f = id`, then a {@link PIso} - * defines an isomorphism between `S` and `T`: + * In addition, if f and g forms an isomorphism between {@code A} and {@code B}, i.e. if {@code f . g = id} and {@code g . f = id}, then a {@link PIso} + * defines an isomorphism between {@code S} and {@code T}: * - *
      + * 
      {@code
        *     S           T                                   S           T
        *     |           |                                   |           |
        *     |           |                                   |           |
      @@ -40,7 +40,7 @@
        *     |           |                                   |           |
        *     |     f     |                                   |     g     |
        *     A --------> B                                   A <-------- B
      - * 
      + * }
      * * A {@link PIso} is also a valid {@link Getter}, {@link Fold}, {@link PLens}, {@link PPrism}, {@link POptional}, * {@link PTraversal} and {@link PSetter} @@ -552,10 +552,10 @@ public PIso reverse() { * create a {@link PIso} between any type and itself. id is the zero element of optics composition, for all optics o of type O * (e.g. Lens, Iso, Prism, ...): * - *
      +   * 
      {@code
          *  o composeIso Iso.id == o
          *  Iso.id composeO o == o
      -   * 
      + * }
      * * (replace composeO by composeLens, composeIso, composePrism, ...) */ diff --git a/core/src/main/java/fj/data/optic/PLens.java b/core/src/main/java/fj/data/optic/PLens.java index 971f76ff..4dcad20b 100644 --- a/core/src/main/java/fj/data/optic/PLens.java +++ b/core/src/main/java/fj/data/optic/PLens.java @@ -17,18 +17,20 @@ import fj.data.vector.V2; /** - * A {@link PLens} can be seen as a pair of functions: - `get: S => A` i.e. from an `S`, we can extract an `A` - `set: (B, S) => - * T` i.e. if we replace an `A` by a `B` in an `S`, we obtain a `T` - * - * A {@link PLens} could also be defined as a weaker {@link PIso} where set requires an additional parameter than reverseGet. - * - * {@link PLens} stands for Polymorphic Lens as it set and modify methods change a type `A` to `B` and `S` to `T`. {@link Lens} - * is a {@link PLens} restricted to monomoprhic updates. - * - * A {@link PLens} is also a valid {@link Getter}, {@link Fold}, {@link POptional}, {@link PTraversal} and {@link PSetter} - * + * A {@link PLens} can be seen as a pair of functions:
        + *
      • {@code get: S => A} i.e. from an {@code S}, we can extract an {@code A}
      • + *
      • {@code set: (B, S) => T} i.e. if we replace an {@code A} by a {@code B} in an {@code S}, we obtain a {@code T}
      • + *
      + *

      + * A {@link PLens} could also be defined as a weaker {@link PIso} where set requires an additional parameter than reverseGet.

      + *

      + * {@link PLens} stands for Polymorphic Lens as it set and modify methods change a type {@code A} to {@code B} and {@code S} to {@code T}. {@link Lens} + * is a {@link PLens} restricted to monomoprhic updates.

      + *

      + * A {@link PLens} is also a valid {@link Getter}, {@link Fold}, {@link POptional}, {@link PTraversal} and {@link PSetter}

      + *

      * Typically a {@link PLens} or {@link Lens} can be defined between a Product (e.g. case class, tuple, HList) and one of it is - * component. + * component.

      * * @param the source of a {@link PLens} * @param the modified source of a {@link PLens} @@ -500,4 +502,4 @@ public F modify(final F f) { } }; } -} \ No newline at end of file +} diff --git a/core/src/main/java/fj/data/optic/POptional.java b/core/src/main/java/fj/data/optic/POptional.java index d35979d8..c70aa4c1 100644 --- a/core/src/main/java/fj/data/optic/POptional.java +++ b/core/src/main/java/fj/data/optic/POptional.java @@ -20,13 +20,15 @@ import fj.data.vector.V2; /** - * A {@link POptional} can be seen as a pair of functions: - `getOrModify: S => T \/ A` - `set : (B, S) => T` - * - * A {@link POptional} could also be defined as a weaker {@link PLens} and weaker {@link PPrism} - * - * {@link POptional} stands for Polymorphic Optional as it set and modify methods change a type `A` to `B` and `S` to `T`. - * {@link Optional} is a {@link POptional} restricted to monomoprhic updates: {{{ type Optional[S, A] = POptional[S, S, A, A] - * }}} + * A {@link POptional} can be seen as a pair of functions:
        + *
      • {@code getOrModify: S => T \/ A}
      • + *
      • {@code set : (B, S) => T}
      • + *
      + *

      + * A {@link POptional} could also be defined as a weaker {@link PLens} and weaker {@link PPrism}

      + *

      + * {@link POptional} stands for Polymorphic Optional as it set and modify methods change a type {@code A} to {@code B} and {@code S} to {@code T}. + * {@link Optional} is a {@link POptional} restricted to monomoprhic updates: {@code type Optional[S, A] = POptional[S, S, A, A]}

      * * @param the source of a {@link POptional} * @param the modified source of a {@link POptional} @@ -479,4 +481,4 @@ public F modify(final F f) { }; } -} \ No newline at end of file +} diff --git a/core/src/main/java/fj/data/optic/PPrism.java b/core/src/main/java/fj/data/optic/PPrism.java index c4a65a68..ffa87ba6 100644 --- a/core/src/main/java/fj/data/optic/PPrism.java +++ b/core/src/main/java/fj/data/optic/PPrism.java @@ -19,14 +19,17 @@ import fj.data.vector.V2; /** - * A {@link PPrism} can be seen as a pair of functions: - `getOrModify: S => T \/ A` - `reverseGet : B => T` - * - * A {@link PPrism} could also be defined as a weaker {@link PIso} where get can fail. - * + * A {@link PPrism} can be seen as a pair of functions:
        + *
      • {@code getOrModify: S => T \/ A}
      • + *
      • {@code reverseGet : B => T}
      • + *
      + *

      + * A {@link PPrism} could also be defined as a weaker {@link PIso} where get can fail.

      + *

      * Typically a {@link PPrism} or {@link Prism} encodes the relation between a Sum or CoProduct type (e.g. sealed trait) and one - * of it is element. + * of it is element.

      * - * {@link PPrism} stands for Polymorphic Prism as it set and modify methods change a type `A` to `B` and `S` to `T`. + * {@link PPrism} stands for Polymorphic Prism as it set and modify methods change a type {@code A} to {@code B} and {@code S} to {@code T}. * {@link Prism} is a {@link PPrism} where the type of target cannot be modified. * * A {@link PPrism} is also a valid {@link Fold}, {@link POptional}, {@link PTraversal} and {@link PSetter} @@ -436,4 +439,4 @@ public Option
      getOption(final S s) { }; } -} \ No newline at end of file +} diff --git a/core/src/main/java/fj/data/optic/PSetter.java b/core/src/main/java/fj/data/optic/PSetter.java index 012920cb..13be7739 100644 --- a/core/src/main/java/fj/data/optic/PSetter.java +++ b/core/src/main/java/fj/data/optic/PSetter.java @@ -5,12 +5,14 @@ import fj.data.Either; /** - * A {@link PSetter} is a generalisation of Functor map: - `map: (A => B) => F[A] => F[B]` - `modify: (A => B) => S => - * T` - * - * {@link PSetter} stands for Polymorphic Setter as it set and modify methods change a type `A` to `B` and `S` to `T`. - * - * {@link PTraversal}, {@link POptional}, {@link PPrism}, {@link PLens} and {@link PIso} are valid {@link PSetter} + * A {@link PSetter} is a generalisation of Functor map:
        + *
      • {@code map: (A => B) => F[A] => F[B]}
      • + *
      • {@code modify: (A => B) => S => T}
      • + *
      + *

      + * {@link PSetter} stands for Polymorphic Setter as it set and modify methods change a type {@code A} to {@code B} and {@code S} to {@code T}.

      + *

      + * {@link PTraversal}, {@link POptional}, {@link PPrism}, {@link PLens} and {@link PIso} are valid {@link PSetter}

      * * @param the source of a {@link PSetter} * @param the modified source of a {@link PSetter} diff --git a/core/src/main/java/fj/data/optic/PTraversal.java b/core/src/main/java/fj/data/optic/PTraversal.java index ee16760f..6087f707 100644 --- a/core/src/main/java/fj/data/optic/PTraversal.java +++ b/core/src/main/java/fj/data/optic/PTraversal.java @@ -24,9 +24,9 @@ /** * A {@link PTraversal} can be seen as a {@link POptional} generalised to 0 to n targets where n can be infinite. - * - * {@link PTraversal} stands for Polymorphic Traversal as it set and modify methods change a type `A` to `B` and `S` to `T`. - * {@link Traversal} is a {@link PTraversal} restricted to monomoprhic updates. + *

      + * {@link PTraversal} stands for Polymorphic Traversal as it set and modify methods change a type {@code A} to {@code B} and {@code S} to {@code T}. + * {@link Traversal} is a {@link PTraversal} restricted to monomoprhic updates.

      * * @param the source of a {@link PTraversal} * @param the modified source of a {@link PTraversal} diff --git a/quickcheck/src/main/java/fj/test/Gen.java b/quickcheck/src/main/java/fj/test/Gen.java index 323995e1..c375874d 100644 --- a/quickcheck/src/main/java/fj/test/Gen.java +++ b/quickcheck/src/main/java/fj/test/Gen.java @@ -33,7 +33,7 @@ * allowing various forms of composition of generators.

      A user typically creates an {@link * Arbitrary arbitrary} to return a generator using the 'combinator methods' below. For example, * suppose a class Person: -

      +
      {@code
       class Person {
         final int age;
         final String name;
      @@ -45,23 +45,23 @@ class Person {
           this.male = male;
         }
       }
      -
      +}
      *

      In a case like this one, a user may create a generator over Person by * invoking the {@link #bind(F)} methods — in this case, {@link #bind(Gen , Gen , F)} the one * that takes two generator arguments}, since the class has one more than two fields (the bind * method is invoked on a generator adding the extra one to the count as they are composed). The * class fields are of types for which there exist generators (on {@link Gen} so those can be * used to compose a generator for Person:

      -
      +
      {@code
       static Gen<Person> personArbitrary() {
         return arbInteger.bind(arbString(), arbBoolean(),
             // compose the generators
             {int age => {String name => {boolean male => new Person(age, name, male)}}};
       }
      -
      +}
      *

      * The example above uses Java 7 closure syntax. Here is the same example using objects instead: -

      +
      {@code
       static Gen<Person> personArbitrary() {
         return arbInteger.bind(arbString, arbBoolean,
             // compose the generators
      @@ -79,7 +79,7 @@ public Person f(final Boolean male) {
               }
             });
       }
      -
      +}
      * * @version %build.number% */ From 6dd03d708f5d04c4f3c0fe4ba25eab39cd075073 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Mon, 8 Oct 2018 14:21:40 +0200 Subject: [PATCH 063/173] html5 javadoc --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index 6104c093..c070903d 100644 --- a/build.gradle +++ b/build.gradle @@ -140,6 +140,10 @@ configure(subprojects.findAll { it.name != "props-core" }) { sourceCompatibility = "1.8" + javadoc { + options.addBooleanOption('html5', true) + } + task javadocJar(type: Jar, dependsOn: "javadoc") { classifier = 'javadoc' from "build/docs/javadoc" From cd201cce91343302d928f72d31ead35021261de9 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Mon, 8 Oct 2018 17:35:49 +0200 Subject: [PATCH 064/173] 4.8.1 release. --- README.adoc | 18 +++++++++--------- build.gradle | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.adoc b/README.adoc index a7e2d3a4..8a3ad347 100644 --- a/README.adoc +++ b/README.adoc @@ -35,12 +35,12 @@ The Functional Java artifact is published to Maven Central using the group `org. * Java 8 specific support (`functionaljava-java8`) * property based testing (`functionaljava-quickcheck` or `functionaljava-quickcheck_1.8` if you use Java 8+) -The latest stable version is `4.8`. This can be added to your Gradle project by adding the dependencies: +The latest stable version is `4.8.1`. This can be added to your Gradle project by adding the dependencies: ---- -compile "org.functionaljava:functionaljava:4.8" -compile "org.functionaljava:functionaljava-java8:4.8" -compile "org.functionaljava:functionaljava-quickcheck:4.8" -compile "org.functionaljava:functionaljava-java-core:4.8" +compile "org.functionaljava:functionaljava:4.8.1" +compile "org.functionaljava:functionaljava-java8:4.8.1" +compile "org.functionaljava:functionaljava-quickcheck:4.8.1" +compile "org.functionaljava:functionaljava-java-core:4.8.1" ---- and in Maven: @@ -48,22 +48,22 @@ and in Maven: org.functionaljava functionaljava - 4.8 + 4.8.1 org.functionaljava functionaljava-java8 - 4.8 + 4.8.1 org.functionaljava functionaljava-quickcheck - 4.8 + 4.8.1 org.functionaljava functionaljava-java-core - 4.8 + 4.8.1 ---- diff --git a/build.gradle b/build.gradle index bc292a82..d6c26032 100644 --- a/build.gradle +++ b/build.gradle @@ -47,8 +47,8 @@ allprojects { defaultTasks "build" ext { - isSnapshot = true - fjBaseVersion = "4.9" + isSnapshot = false + fjBaseVersion = "4.8.1" snapshotAppendix = "-SNAPSHOT" fjVersion = fjBaseVersion + (isSnapshot ? snapshotAppendix : "") From e32fbfdfee3b0f939ec3a9a83cb2c2d094ff3f64 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Mon, 8 Oct 2018 17:39:52 +0200 Subject: [PATCH 065/173] Set version to 4.9-SNAPSHOT --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index d6c26032..67d8cd15 100644 --- a/build.gradle +++ b/build.gradle @@ -47,12 +47,12 @@ allprojects { defaultTasks "build" ext { - isSnapshot = false - fjBaseVersion = "4.8.1" + isSnapshot = true + fjBaseVersion = "4.9" snapshotAppendix = "-SNAPSHOT" fjVersion = fjBaseVersion + (isSnapshot ? snapshotAppendix : "") - fjConsumeVersion = "4.8" + fjConsumeVersion = "4.8.1" signModule = false useRetroLambda = false From fcff9d1dc166943906aaa837b26478e74814bfd9 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Mon, 8 Oct 2018 18:11:11 +0200 Subject: [PATCH 066/173] Update changelog with 4.8.1 changes. --- ChangeLog.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 24276f35..acdbedea 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,23 @@ +4.8.1 +----- + +### Enhancements + +- Add Trampoline.suspend(final F0> a). (see [#367](https://github.com/functionaljava/functionaljava/pull/367)); + +### Fixes + +- Fix regression in lifted semigroup sum. Fix #365 (see [#366](https://github.com/functionaljava/functionaljava/pull/366)); + +### Internal + +- Fix compile under jdk11. Enable jdk11 travis build. (see [#361](https://github.com/functionaljava/functionaljava/pull/361)); +- Fix warnings (see [#369](https://github.com/functionaljava/functionaljava/pull/369)); +- Add P tests (see [#360](https://github.com/functionaljava/functionaljava/pull/360)); +- Exclude consume/ from coverage (see [#357](https://github.com/functionaljava/functionaljava/pull/357)); +- Add DList tests (see [#356](https://github.com/functionaljava/functionaljava/pull/356)); +- Add Visitor tests (see [#354](https://github.com/functionaljava/functionaljava/pull/354)); + 4.8 --- @@ -20,7 +40,6 @@ - Added Scalacheck Arbitrary implementations for Natural and NonEmptyList. (see [`405c3ec`](https://github.com/functionaljava/functionaljava/commit/405c3ec)); - Added unit test coverage for Semigroup implementations. The StringBuffer and StringBuilder tests fail because both of those types are mutable. The IO test fails because the ArbitraryIO implementation does not implement equals. (see [`ef81130`](https://github.com/functionaljava/functionaljava/commit/ef81130)); - Fixed the ArbitraryIO implementation and created a Properties object for testing the IO semigroup. (see [`a8e979f`](https://github.com/functionaljava/functionaljava/commit/a8e979f)); -- Added working tests coverage for the StringBuffer and StringBuilder semigroup implementations. Added the Semigroup tests to the list of Scalacheck test suite. (see [`aa4de33`](https://github.com/functionaljava/functionaljava/commit/aa4de33)); - Equal: remove reference to static field of LazyString. Fix #321 (see [`6c6dabd`](https://github.com/functionaljava/functionaljava/commit/6c6dabd)); - Add IOFunctions tests (see [#340](https://github.com/functionaljava/functionaljava/pull/340)); - Add Stream tests (see [#341](https://github.com/functionaljava/functionaljava/pull/341)); @@ -30,4 +49,4 @@ - Add Parser tests (see [#349](https://github.com/functionaljava/functionaljava/pull/349)); - Add FingerTree tests (see [#351](https://github.com/functionaljava/functionaljava/pull/351)); - Add TreeZipper tests (see [#352](https://github.com/functionaljava/functionaljava/pull/352)); -- Add Reader/Writer tests (see [#353](https://github.com/functionaljava/functionaljava/pull/353)); \ No newline at end of file +- Add Reader/Writer tests (see [#353](https://github.com/functionaljava/functionaljava/pull/353)); From 86dabd508306a2cc78fbb692994e158420926c8d Mon Sep 17 00:00:00 2001 From: SoundharyaKamaraj Date: Thu, 25 Oct 2018 12:37:50 -0400 Subject: [PATCH 067/173] #371: fixed failing property test (#373) --- .../src/test/java/fj/data/hamt/BitSetProperties.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/props-core/src/test/java/fj/data/hamt/BitSetProperties.java b/props-core/src/test/java/fj/data/hamt/BitSetProperties.java index f3527972..6c3f91a9 100644 --- a/props-core/src/test/java/fj/data/hamt/BitSetProperties.java +++ b/props-core/src/test/java/fj/data/hamt/BitSetProperties.java @@ -156,15 +156,19 @@ Property rangeTest() { int h = list.index(2); int m = Math.max(l, Math.min(list.index(1), h - 1)); int vh = list.index(3); - + BitSet bs1 = longBitSet(x); BitSet bs2 = bs1.range(l, h); + if(l==h){ + return prop(true); + } boolean b = - bs1.isSet(m) == bs2.isSet(m - l) && + bs1.isSet(m) == bs2.isSet(m - l) && bs2.isSet(vh - l) == false; return prop(b); }); } + Property setTest() { return property(arbNaturalLong, arbBitSetSize, (l, i) -> prop(longBitSet(l).set(i).isSet(i))); From c0f0fdbc6ba6e0dd1043fe63d07af4cac6a0c5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sat, 15 Dec 2018 22:21:15 -0500 Subject: [PATCH 068/173] Add Validation tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/main/java/fj/data/Validation.java | 8 +- .../src/test/java/fj/data/ValidationTest.java | 244 ++++++++++++++++++ 2 files changed, 248 insertions(+), 4 deletions(-) create mode 100644 core/src/test/java/fj/data/ValidationTest.java diff --git a/core/src/main/java/fj/data/Validation.java b/core/src/main/java/fj/data/Validation.java index 58aa5e9c..0b939e57 100644 --- a/core/src/main/java/fj/data/Validation.java +++ b/core/src/main/java/fj/data/Validation.java @@ -783,7 +783,7 @@ public final Validation, D> accumulate(Validation v2, Va public final Validation, G> accumulate(Validation v2, Validation v3, Validation v4, Validation v5, Validation v6, F6 f) { - List list = fails(list(this, v2, v3, v4, v5)); + List list = fails(list(this, v2, v3, v4, v5, v6)); if (!list.isEmpty()) { return fail(list); } else { @@ -792,7 +792,7 @@ public final Validation, D> accumulate(Validation v2, Va } public final Validation, H> accumulate(Validation v2, Validation v3, Validation v4, Validation v5, Validation v6, Validation v7, F7 f) { - List list = fails(list(this, v2, v3, v4, v5)); + List list = fails(list(this, v2, v3, v4, v5, v6, v7)); if (!list.isEmpty()) { return fail(list); } else { @@ -801,7 +801,7 @@ public final Validation, D> accumulate(Validation v2, Va } public final Validation, I> accumulate(Validation v2, Validation v3, Validation v4, Validation v5, Validation v6, Validation v7, Validation v8, F8 f) { - List list = fails(list(this, v2, v3, v4, v5)); + List list = fails(list(this, v2, v3, v4, v5, v6, v7, v8)); if (!list.isEmpty()) { return fail(list); } else { @@ -860,7 +860,7 @@ public static List fails(List> list) { return list.filter(Validation::isFail).map(v -> v.fail()); } - public static List
      successes(List> list) { + public static List successes(List> list) { return list.filter(Validation::isSuccess).map(v -> v.success()); } diff --git a/core/src/test/java/fj/data/ValidationTest.java b/core/src/test/java/fj/data/ValidationTest.java new file mode 100644 index 00000000..2d90c9e4 --- /dev/null +++ b/core/src/test/java/fj/data/ValidationTest.java @@ -0,0 +1,244 @@ +package fj.data; + +import org.junit.Test; + +import static fj.data.Validation.*; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; +import fj.*; + +public class ValidationTest { + @Test + public void testParseShort() { + final List> l = + List.list(parseShort("10"), parseShort("x"), parseShort("20")); + assertThat(successes(l).foldLeft1((s, a) -> (short)(s + a)), is((short)30)); + } + + @Test + public void testParseLong() { + final List> l = + List.list(parseLong("10"), parseLong("x"), parseLong("20")); + P2, List> p2 = partition(l); + assertThat(p2._1().length(), is(1)); + assertThat(p2._2().length(), is(2)); + } + + @Test + public void testParseInt() { + final List> l = + List.list(parseInt("10"), parseInt("x"), parseInt("20")); + assertThat(l.map(v -> v.validation(e -> 0, i -> 1)).foldLeft1((s, a) -> s + a), + is(2)); + } + + @Test + public void testParseFloat() { + final List> l = + List.list(parseFloat("2.0"), parseFloat("x"), parseFloat("3.0")); + assertThat(l.map(v -> v.validation(e -> 0, i -> 1)).foldLeft1((s, a) -> s + a), + is(2)); + } + + @Test + public void testParseByte() { + final List> l = + List.list(parseByte("10"), parseByte("x"), parseByte("-10")); + assertThat(l.map(v -> v.validation(e -> 0, i -> 1)).foldLeft1((s, a) -> s + a), + is(2)); + } + + @Test + public void testAccumulate1() { + final Validation, Double> v = + parseDouble("10.0").accumulate( + f1 -> f1); + assertThat(v.success(), is(10.0)); + } + + @Test + public void testAccumulate1Fail() { + final Validation, Double> v = + parseDouble("x").accumulate( + f1 -> f1); + assertThat(v.fail().length(), is(1)); + } + + @Test + public void testAccumulate2() { + final Validation, Double> v = + parseDouble("1.0").accumulate( + parseDouble("2.0"), + (f1, f2) -> f1 + f2); + assertThat(v.success(), is(3.0)); + } + + @Test + public void testAccumulate2Fail() { + final Validation, Double> v = + parseDouble("x").accumulate( + parseDouble("y"), + (f1, f2) -> f1 + f2); + assertThat(v.fail().length(), is(2)); + } + + @Test + public void testAccumulate3() { + final Validation, Double> v = + parseDouble("1.0").accumulate( + parseDouble("2.0"), + parseDouble("3.0"), + (f1, f2, f3) -> f1 + f2 + f3); + assertThat(v.success(), is(6.0)); + } + + @Test + public void testAccumulate3Fail() { + final Validation, Double> v = + parseDouble("x").accumulate( + parseDouble("2.0"), + parseDouble("y"), + (f1, f2, f3) -> f1 + f2 + f3); + assertThat(v.fail().length(), is(2)); + } + + @Test + public void testAccumulate4() { + final Validation, Double> v = + parseDouble("1.0").accumulate( + parseDouble("2.0"), + parseDouble("3.0"), + parseDouble("4.0"), + (f1, f2, f3, f4) -> f1 + f2 + f3 + f4); + assertThat(v.success(), is(10.0)); + } + + @Test + public void testAccumulate4Fail() { + final Validation, Double> v = + parseDouble("x").accumulate( + parseDouble("2.0"), + parseDouble("3.0"), + parseDouble("y"), + (f1, f2, f3, f4) -> f1 + f2 + f3 + f4); + assertThat(v.fail().length(), is(2)); + } + + @Test + public void testAccumulate5() { + final Validation, Double> v = + parseDouble("1.0").accumulate( + parseDouble("2.0"), + parseDouble("3.0"), + parseDouble("4.0"), + parseDouble("5.0"), + (f1, f2, f3, f4, f5) -> f1 + f2 + f3 + f4 + f5); + assertThat(v.success(), is(15.0)); + } + + @Test + public void testAccumulate5Fail() { + final Validation, Double> v = + parseDouble("x").accumulate( + parseDouble("2.0"), + parseDouble("3.0"), + parseDouble("4.0"), + parseDouble("y"), + (f1, f2, f3, f4, f5) -> f1 + f2 + f3 + f4 + f5); + assertThat(v.fail().length(), is(2)); + } + + @Test + public void testAccumulate6() { + final Validation, Double> v = + parseDouble("1.0").accumulate( + parseDouble("2.0"), + parseDouble("3.0"), + parseDouble("4.0"), + parseDouble("5.0"), + parseDouble("6.0"), + (f1, f2, f3, f4, f5, f6) -> f1 + f2 + f3 + f4 + f5 + f6); + assertThat(v.success(), is(21.0)); + } + + @Test + public void testAccumulate6Fail() { + final Validation, Double> v = + parseDouble("x").accumulate( + parseDouble("2.0"), + parseDouble("3.0"), + parseDouble("4.0"), + parseDouble("5.0"), + parseDouble("y"), + (f1, f2, f3, f4, f5, f6) -> f1 + f2 + f3 + f4 + f5 + f6); + assertThat(v.fail().length(), is(2)); + } + + @Test + public void testAccumulate7() { + final Validation, Double> v = + parseDouble("1.0").accumulate( + parseDouble("2.0"), + parseDouble("3.0"), + parseDouble("4.0"), + parseDouble("5.0"), + parseDouble("6.0"), + parseDouble("7.0"), + (f1, f2, f3, f4, f5, f6, f7) -> f1 + f2 + f3 + f4 + f5 + f6 + f7); + assertThat(v.success(), is(28.0)); + } + + @Test + public void testAccumulate7Fail() { + final Validation, Double> v = + parseDouble("x").accumulate( + parseDouble("2.0"), + parseDouble("3.0"), + parseDouble("4.0"), + parseDouble("5.0"), + parseDouble("6.0"), + parseDouble("y"), + (f1, f2, f3, f4, f5, f6, f7) -> f1 + f2 + f3 + f4 + f5 + f6 + f7); + assertThat(v.fail().length(), is(2)); + } + + @Test + public void testAccumulate8() { + final Validation, Double> v = + parseDouble("1.0").accumulate( + parseDouble("2.0"), + parseDouble("3.0"), + parseDouble("4.0"), + parseDouble("5.0"), + parseDouble("6.0"), + parseDouble("7.0"), + parseDouble("8.0"), + (f1, f2, f3, f4, f5, f6, f7, f8) -> f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8); + assertThat(v.success(), is(36.0)); + } + + @Test + public void testAccumulate8Fail() { + final Validation, Double> v = + parseDouble("x").accumulate( + parseDouble("2.0"), + parseDouble("3.0"), + parseDouble("4.0"), + parseDouble("5.0"), + parseDouble("6.0"), + parseDouble("7.0"), + parseDouble("y"), + (f1, f2, f3, f4, f5, f6, f7, f8) -> f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8); + assertThat(v.fail().length(), is(2)); + } + + @Test(expected = Error.class) + public void testSuccess() { + parseShort("x").success(); + } + + @Test(expected = Error.class) + public void testFail() { + parseShort("12").fail(); + } +} From 0e8e15232f4953a9396e7b30e5664dc1fb513a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sun, 16 Dec 2018 13:35:39 -0500 Subject: [PATCH 069/173] Additional Validation tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- .../src/test/java/fj/data/ValidationTest.java | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/core/src/test/java/fj/data/ValidationTest.java b/core/src/test/java/fj/data/ValidationTest.java index 2d90c9e4..2f438e5c 100644 --- a/core/src/test/java/fj/data/ValidationTest.java +++ b/core/src/test/java/fj/data/ValidationTest.java @@ -1,11 +1,12 @@ package fj.data; +import fj.P2; import org.junit.Test; +import static fj.Semigroup.firstSemigroup; import static fj.data.Validation.*; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; -import fj.*; public class ValidationTest { @Test @@ -232,6 +233,47 @@ public void testAccumulate8Fail() { assertThat(v.fail().length(), is(2)); } + @Test + public void testAccumulate8s() { + final Validation v1 = parseInt("1"); + final Validation v2 = parseInt("2"); + final Validation v3 = parseInt("3"); + final Validation v4 = parseInt("4"); + final Validation v5 = parseInt("5"); + final Validation v6 = parseInt("6"); + final Validation v7 = parseInt("7"); + final Validation v8 = parseInt("8"); + final Option on2 = v1.accumulate(firstSemigroup(), v2); + assertThat(on2, is(Option.none())); + final Option on3 = v1.accumulate(firstSemigroup(), v2, v3); + assertThat(on3, is(Option.none())); + final Option on4 = v1.accumulate(firstSemigroup(), v2, v3, v4); + assertThat(on4, is(Option.none())); + final Option on5 = v1.accumulate(firstSemigroup(), v2, v3, v4, v5); + assertThat(on5, is(Option.none())); + final Option on6 = v1.accumulate(firstSemigroup(), v2, v3, v4, v5, v6); + assertThat(on6, is(Option.none())); + final Option on7 = v1.accumulate(firstSemigroup(), v2, v3, v4, v5, v6, v7); + assertThat(on7, is(Option.none())); + final Option on8 = v1.accumulate(firstSemigroup(), v2, v3, v4, v5, v6, v7, v8); + assertThat(on8, is(Option.none())); + } + + @Test + public void testAccumulate8sFail() { + final Option on = + parseInt("x").accumulate( + firstSemigroup(), + parseInt("2"), + parseInt("3"), + parseInt("4"), + parseInt("5"), + parseInt("6"), + parseInt("7"), + parseInt("y")); + assertThat(on.some().getMessage(), is("For input string: \"x\"")); + } + @Test(expected = Error.class) public void testSuccess() { parseShort("x").success(); @@ -241,4 +283,36 @@ public void testSuccess() { public void testFail() { parseShort("12").fail(); } + + @Test + public void testCondition() { + final Validation one = condition(true, "not 1", "one"); + assertThat(one.success(), is("one")); + final Validation fail = condition(false, "not 1", "one"); + assertThat(fail.fail(), is("not 1")); + } + + @Test + public void testNel() { + assertThat(Validation.success("success").nel().success(), is("success")); + assertThat(Validation.fail("fail").nel().fail().head(), is("fail")); + } + + @Test + public void testFailNEL() { + Validation, Integer> v = failNEL(new Exception("failed")); + assertThat(v.isFail(), is(true)); + } + + @Test + public void testEither() { + assertThat(either().f(Validation.success("success")).right().value(), is("success")); + assertThat(either().f(Validation.fail("fail")).left().value(), is("fail")); + } + + @Test + public void testValidation() { + assertThat(validation().f(Either.right("success")).success(), is("success")); + assertThat(validation().f(Either.left("fail")).fail(), is("fail")); + } } From 148eab718a81bfd90e5fd8176fb6c4435602da3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sun, 16 Dec 2018 14:12:00 -0500 Subject: [PATCH 070/173] Bring Gradle current MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- build.gradle | 4 ++-- gradle/wrapper/gradle-wrapper.jar | Bin 56177 -> 56177 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index c070903d..37a7b7c3 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } wrapper { - gradleVersion = "4.10.2" + gradleVersion = "4.10.3" distributionType = Wrapper.DistributionType.ALL } } @@ -69,7 +69,7 @@ allprojects { sonatypeUploadUrl = isSnapshot ? sonatypeSnapshotUrl : sonatypeReleaseUrl primaryEmail = "functionaljava@googlegroups.com" - dependencyJunit = "org.junit.vintage:junit-vintage-engine:5.2.0" + dependencyJunit = "org.junit.vintage:junit-vintage-engine:5.3.2" displayCompilerWarnings = true } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 29953ea141f55e3b8fc691d31b5ca8816d89fa87..94336fcae912db8a11d55634156fa011f4686124 100644 GIT binary patch delta 75 zcmeykjrrp?<_YF3{B|p=CR)d^elGf0#5nPQ^v3PRCxzG;0=(Hd{+{0VNQ{AjK?H~= bKR6{bS>lv7Sp8=AQ}+Z|K+=;nZ+id$gFGJ; delta 75 zcmeykjrrp?<_YF3`^8VXPqdC<{aEy|h;iZp>5bcsPYN+H1bDM^WdGhdRg8gwK?H~= bKR6{bS>lv7Sp8=AQ}+Z|K+=;nZ+id$ib5We diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d76b502e..ae45383b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From f85a3698a2b2838c0cc6462c85817a4c06c829d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Thu, 28 Feb 2019 20:29:08 -0500 Subject: [PATCH 071/173] Bring Gradle to 5.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- build.gradle | 5 +++-- consume/build.gradle | 3 ++- core/build.gradle | 3 ++- demo/build.gradle | 3 ++- gradle/wrapper/gradle-wrapper.jar | Bin 56177 -> 55190 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- gradlew.bat | 2 +- java-core/build.gradle | 3 ++- performance/build.gradle | 3 ++- props-core-scalacheck/build.gradle | 3 ++- props-core/build.gradle | 5 +++-- quickcheck/build.gradle | 2 +- 13 files changed, 22 insertions(+), 14 deletions(-) diff --git a/build.gradle b/build.gradle index 37a7b7c3..b206336a 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } wrapper { - gradleVersion = "4.10.3" + gradleVersion = "5.2.1" distributionType = Wrapper.DistributionType.ALL } } @@ -69,7 +69,8 @@ allprojects { sonatypeUploadUrl = isSnapshot ? sonatypeSnapshotUrl : sonatypeReleaseUrl primaryEmail = "functionaljava@googlegroups.com" - dependencyJunit = "org.junit.vintage:junit-vintage-engine:5.3.2" + junitCompile = "junit:junit:4.12" + junitRuntime = "org.junit.vintage:junit-vintage-engine:5.3.2" displayCompilerWarnings = true } diff --git a/consume/build.gradle b/consume/build.gradle index ff92a601..370ebfbf 100644 --- a/consume/build.gradle +++ b/consume/build.gradle @@ -4,5 +4,6 @@ archivesBaseName = "${project.projectName}-${project.name}" dependencies { compile("$group:$projectName:$fjConsumeVersion") - testCompile dependencyJunit + testCompile junitCompile + testRuntime junitRuntime } diff --git a/core/build.gradle b/core/build.gradle index 13d06114..767904e0 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -6,7 +6,8 @@ ext { archivesBaseName = project.projectName dependencies { - testCompile dependencyJunit + testCompile junitCompile + testRuntime junitRuntime testCompile 'com.h2database:h2:1.4.197' testCompile 'commons-dbutils:commons-dbutils:1.7' } diff --git a/demo/build.gradle b/demo/build.gradle index e97f94b8..f19e765f 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -8,7 +8,8 @@ archivesBaseName = "${project.projectName}-${project.name}" dependencies { compile project(":core") compile project(":quickcheck") - testCompile dependencyJunit + testCompile junitCompile + testRuntime junitRuntime } test { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 94336fcae912db8a11d55634156fa011f4686124..87b738cbd051603d91cc39de6cb000dd98fe6b02 100644 GIT binary patch delta 46897 zcmY(oV{o8N6s?dNGN54433q4-*eEE_ARxbffq;Mrf!Nja^9lZU6G#x0L!l1FAVWtjLcepq zbN&JHzYAL7fBetF4YdDpGNb;V>WVe4L++g^8LiRitE9h1FK{eH^FRgq< zeMRwGy=43qL=LQ^UCv)B?g%YF8NMGF_(OKwQ)WaAyt}Pvm$`;A zeL$^VmgW~xp&>*gVBEeOa3Or}4tZ>9Y8Fj-I?xpj4sKCTc`zbBbQ>G33uzDSA!FQ5 zmb!uGsnPG_X%M;{U{*gx!xHe=9Pj8jaJKkE zq>H2zY7$XAiT9b4!|qm@KHU*(gf{9IVN}Y1MNaT?KbK7`wz?snA#^9AUoVq9acyoA zD*ydhajp35wc-2*>M_4alM-Ex!3lNH6EeK@IG|gyCyALxX`?VyS4z}Eft)}52J4}5 z^;5(kC~!~%kj?Q}_`)^EPA+`PMeqR=k0ArsMN3T{+$PHnD~&w=4mnyrzhGOFDF6wu1F+ zH;59n?pZLP&lVt1^p%uz`Va6)*YGCR*mHYiBe8cpoD?2gh^ zB0VMy%6aT z%94Ql>rU6qF1^}}aarDaOox>vv48Rxv7HGSgoKFAWG%NYKmqN3R2l1t+BKRv@UEQT8`7C%HzH=R z55C7Ox4v>Rd^fag)C_Kjr>&U7v9U^PS1;9LIV%c#k>i)v@ornl@U74KRuPq`Bc{68 z)y4!Xp#vd}BZ_Zrt?NWzVoKazoM9@=Ss8V`;*gLhKza9@M*jmGWjuzVoki-Km=~-oOd>n>~dl7wTC;debf+Xed6L|vU zwf|AR{uppV9DJM;i1F7QFrpP4v>VXQLrM5R6X^H8H|B^&LO>j)R0bmz?!}pJf=L(# z|1%_mdc;~?ddnPu&aZHQHqWh6Iw0VaGObS{C9vuOO+JAcKSx)%@(;O5oz3CCEc1TAxPYm+kGE zi0{{c6t>k^4rKry?$dp4Rvi{_gu2Y!*g6&6lwJa*k$3X$p zw3Kr|WVxE_=T+=P@(xXp=D_43yJa|=V|(U4_yGQ=e*SNvwN(1@EBH@n=l|OzNq2!RfoMbJjh&;G3E5FV*A$NAyNg4QDKb*L=O&sCM!6e7g=75n~xYW*rC1M}BCrArHYPwhPo@|T*+Ry?tI6A>sfJ*E?hRVjz#?)$t_3iIogdPi3} z8`9Du(s0FO!G)|-b47w1f>gUrPvbG`nfcct4n0lZP^lNV>&=cQ4b-_S+DmUvj zif}uwS9Qt&>$Tzi%MEd6&mr(t$5)IE&=9i%9QFwUUnM@00s!_qZ^W!S27jOoc={bt z3vpNb+G_p5^Fa=q52SN-BX_FK$UHu zDctxjCR@%}JdbFA`0q|j<>N#8H?475jueNK8loODYcY4Yjd6yuiDWhLQiig_I5VgM zCNlK6vzF1gbn^wZh&oQgNHS1IWdo;WG5$^%Y8pg^BiAl=g_BR-@HbgUu6-(bs>GJ* z$X%r+b3TuK)m*ZWNbYAPO@ia*bX&GG7nM-ijGTTBSNt=1b}$A`b9WI<8O|lw_JU8v z$>wbSm!*aS!dl172F6uOTXwo-l(#)CQ@w)XLWXP+FV7y_+Fg^0bR&@0Qrr6?`JHe1 zsC%MhPaIz(ePko#HoChv81_CR)+tX=0fsFJJ(Prk0)JF}A>V;whf>3b?y*ka{`}Z< z(BgI>70JYTU0;g(p4Lq?DmR=6CG^>p^~W(=wSp)0{!j3FY4IjMWKtUPT@l}M>+ftH z$m>`#<86N-i9uv+T6VyX$!sIOZzTh_Fwt$RK6oK12is}$hY#sB#4&@5`SkoNmfI z+<)(ytO-78gW{1tnz#4uui4U7&ywf54uGOaD~J7%^!C*?lO4{0KTncWc;b_AKec)QokfkA-JTW;W2DYc*HQw4H*K4SdCe|f63#HYl6*FXg0@c7>ycf1z z*9#Z$d?FXnm;ibMt@JDDNd@eAW64S)yHHyW(=Z~Qa))tz4~VUF!-Jm&4mzg_+@$7X zc*%0P9;@Z6rvsk$G&nWG;$?l{5-=vAx?IgPOgW?_(W_JzFEt_&N_sz9IggY~r$W$|JrQu9@Boou;1!&mJoljte4Kj7nVtkpg8#yeRw?oxOpvu-Q&$_}SnOswH z3}kxRDAa567L|r#B5Qbok?e=0BdFRM7LZ(;y6ggKdEklVw7=EKRECE@uPKvwG383O zlq6CxT>zfk)oN=3n6$exYy8GtVzd51FBq#O-61lVow(7{9;ulu`)qZ?d{&M_c^UTu zlE7uMU8{$EmoR73I3f?N290&PH3|?Y8h2)W91ZN!q1qk)=_ka_WTY zCxSLJX(_B%6xpEjdLoOnI*815+7B6gMWeb%l$k`A^u|M>rB&$}q$M1+DNLN}3(YY@ zbzoOxPDYEF9wm^b6#ntW^$9luwj|v$p%4_*4=DyaL3uTtl!4oRNM3OxnPt3?m^ls> zi2>^`iA!)4u|yLni3xA`=BlLjU6c6xK0K2Moy@SNQi7|HVN;4$*$~;*>L6FVc6S{F z_k4X@&8(n0-%+*{j6htRCbEipP8v+=5D@aFSi9V!Y}?f=GMG#TWmFDv(jO++n>S6D zks9XQuG;19VkBmV?hBEq72@n1Ni>GIl9*f<`56Qnrjx#6)vh^^X3)_EOAkdH>D~Tx zuiE8Cnu=+|XX(?z!E%L|eCFqeO_y~Hp|9QykDql7i{$Fx=#Q^mPgQC;^Y5X(1nANu zXDlBpPUmPB-8CtfBt8>UK07-jb;FE`XBYVh$Hn2!e|SPppw(fnZckKd9tJH-UQHY1 zLu;Ezoh+kX6kPUZg0xY$PY$Z=xOBA6F(y^1)rS$taR*0#!Zv*{&Q9;(@5p(kbZCo^$?M+G^aw0v&!ZuoT($5TGj~g@p<3s`U=A1MKN?*#9jP25fsxHnS!nQs_7#Q7*Xpc)Cn;%$V{>oSb04nFnjpqb5 zx&iAT+>wK>bxrCqc_e7Pdw&iYMjK-Fxd*=VgFE+4S+&_U*U`;lb5j#(Pi2%WUC*`I zjc9gptK$0huue&3ZP`~S3!b&6K_YVdhRwR#_GFw41AA_1;SQ-I6y$d4+t0l$-mEvWjU{ zAXC0lS${AS?;?+@~CCw{+ludBhNSy z__e8FK9wP66X~J#*rfT8eSA8qxo>>`Q>3>wnqVf`$*z4g+*xxpy42rUOKn>C3EobZ zTLOA4*RO=bl^%@-Nb!4^N$m5S&u-`jCr`dp#X@n`T@8LZ4d@F9U^M2O`2q()3pb%F ziKmQC!d1YCArO}A#$QD!hYbEIaWUX0d4)@SVK!gopPVE7xV_U^S|y#LYmCQuVvn;N zfo{n%+*j{P=^mja=|ON<5V%mY5-RYKRt>qPGD^`ND&#@}ZVsd0i5yK1XsNNYYVsTE zkFb4XjCR;wak6TO`k+kouf?(9Y)hKxHgDV)jcL^L0%BJ~;TDC@8fZFAgwkTb_1LXh zXk2F&I?8h9$0eurn!eJ#opD|8B-%DmR0Ff34MqA{4p<3vP=Rbya+fVY9cmsR$~!ky=%k zP>{@;OV>8G(ErTqGwBT$?1TOC9aqN;?N-zJJKVp41_1_3 z#W~+he)%0r8LZtz-d!>|w@-x>)TW@MU#2=lZB@k^Y35H$sH_y4N@h#{z2nZO%v4zI z!7m(uaqtUvUE4k{A^D17x8=Fx{vk9IVWs<{K)z$rG&F}9TmK%*lHHN9%?B&PkV81Sz!`y1 zKtMotGRpK@!zr>s;Q)p+h+&VmCmQk=?N1?qUV7&?zul*W#7pS^Cy9F1*hsXrFHSj2 zyDQM+Kmv0y+Y>#G#R*QjXP})TogSfK8HfhdlHb`sIDLf1eZd3*az)wPx}sm|G(>Fl z2NmYjbETtwrR&pVsGb|B)QFZmk#9tb%bKQZZlfYfEt3o=UMkBv{>IqAR&mq~_OSpE zIc&H7pfgyW+k(9TD2N0{o2D`J!(^EkIHzq=a-dOIP)ZzDa(`h_^$zf{z=VZSK zuvRyV_{?7DsBu+3+Y&104;~AsYvo*4nKsk=<&9h*zuoU{? z&j>U7+p~UHxR#`-9=BOS6qPRcaSf~KMfeM@OCqkO(3KG`NCn$`L)~6~whj6cKEZL< z;xI4`xuHDvPFUG{^i14`amvi2)ls8YWyyPP8WpLWpy3>kojMK-A;RQY!{7l6M^e|O z`-Ri-T}=g-(>836f1LO=u-V~>tJgM2(=Vp88`lW`2K?opVKvx8-kCY2Lbo+$ffO3Z z#qs|66rk2XiM;sT0;@%KVYo?L4Mv1NSRoz1q zNwk&z=!>K>XG-V`CbXnSwT-}IAGY(y&xzK=%~mFBf?}NK#CMrjGjYd6{^@yqlf~M7 zvFCRvWC9Zfqvr!~1A&^)ahhL80B&wx?D(KuS)0^d3O9!q^opvqgu}h=9hxMmC3Tu9 zV-`fj>>u{zUjs_%-Z<@LQI(gA*;5-IS9+Q-<%PFz}G3`0x37{uSSbt z&^e<ejF{}zs7Rs z6Y4R;6>K(y!G6zJZVUv!r#-9~uk?_lT<*>V6x@p`3q< z$rHcY{ZG`Bw2gW5f01<}F5h1*5FjAINxN7)fX0Rkjt15@p4NZX*3H+dLVZe3UYnYy zJ_SNL(pDIEMgB-|I=%z}hunuKw%dDcIXFdWf%av>;sb}b8 z`6S~Y4lM8^&TlUS3zM<8k%p^>OqTC?&86pIt9R$+>RsXc^OFDsx4+hvH_}2o??9^= zn6&nkmq0aGbd;CCohchkTfFWHg_-iyx04h5qaq_jC)O+phmplp+XBPQZFsimE;VS( zVv8p=oFghFfgju9kjOQz5^?1U1c#c264iy!2}54lMN!4bEWcmM!4< z?r(lQ49VNw9*l~Gg!ugov}>#ZGeedZu;F^#sM2G>_3-(#TBJthPHr@p>8b+N@~%43 zwem|;Oz;rgJy~yKny2THKC6Xz1d?|2J`QS#`OWtwTF`ZjPPxu*)A_L6+DhJg;rI_- z3SX)Ir^5)&2#7g4#|)BlrrHDxU(kzx!B|omqXakKSUEOPXoF#`s}M z!x0_JkWz}vB1!w{GY0EQkzHDP9t)dcb$`1ECxOG)zOfJ~LhJUntqyW?gREucjM_oI}cI zmM*hh?i%aY_JCF=?Lh|E4`dCkSstOob`WVgEVyMs<5solO7 zzBVDG?wB<6i150(92oObwL33a%@jA2${2WrX*J!>^NEF$UP8xj_gJM|`RQg{ceaAg z(ZzXR2QJz3?M~+vpw#tWXq8OTTZEb|)x>4WWLZNGPg1Xjd%>T$=i{QH)g!7I6Kv}ynehHw5i?zEuZN;JE0)6njlX2*Tc#(Sxud=Vj zi0MfE@>r3M-<w~Xd2oCryVQs?FQpr2&aB~eiiNB**$L&Ya=lsHGfBI@%IwZqIR1vGy@(RlGF;6^;Yd$;*BDU?K z)D7tp;EyvIn)5&Z`e`LM7VhrN)+Z_V5z~`RSP}UO6M9Q3U>rteUnLc*cuz{DGW!*D zfL-vh)fky=IhO}HS}I%+S`^AlP}tY&R4_Bo*fLH(-*N;Rgg@xn9fxT;OIi9ls-e+c z8Vlwj_#k(=69HPhQ_ZHro0kihXcWx*)aUl3MqfFRd%}YJcqn0c)gB@Nwk_oP!IxNQ zYFoTkBB+O4pBM7B^YiS)%O&>n?SuQtE*i*GNP&V&fQo78;>7V}eGvG6@(RS+Iy#j9d#msGejrzrbKA3am`h?^2|< zyn_28ScVYc(8&Dx@N$ytVoTuL|LZ4X5IDF}w3H{giEcmGJ~A@2aC-8cZk2)PY#bg)+Fmzjx~6GwBBfgA%sUmE>kKO%Xu(!vf4LFZ%TR|&9hE!^NH0hEarTTu!#6~-y!*fhJGl~z;KzQ{Su7pry zwVtE3?J^H##z;l_Q+v;iGCC<(krs_et3xSVFAyN>S9%6xO6#uabVXt@c-Vw}Cq#OC4;&6ll2wOigKR zy;tp~vjRq#aXa0d5#}D{rC15}!s+WyC+W~mmhuTk=lXOP@*ylc-O2wLw@uk|W(ULP zajeB;adFg`Tvf>Vm*{?MyO_`+4GdWnuPTJgXDGqTaXCNFRqskyNoos2J!|?~1N2t3T8CU7)EH~Akf(uCE0qjnr z>a=}J!pn?$S=X!_cIdAi9WGvwt@zw@ zdv_l@zW^)~4Q4+-u3wT`A9`N8zgK!+auE2OkFbAND;GyoP_0H$&?q{_VdfBrmX3R7 z1|kd5_b-?Mydopr`n(HK7El9?XuOa!Py1f_f70%J!K1(qh`R)7dldVl9CM>#pi{XI zF=#xY^r;OE$V1bj_Ryf|_x-VIANSStyR-v>SH?W61KB*Y1FgICM{rQ9P(U#5#IYRi z_otwLdPW68-sZh}S7L6Hhqn6M!82@^=b_#pKX}jpZNU@J+d(hXo&?*qgb~TnQdk6v zbub#JvWDYP2$66a-EHIt9j_npnjdwVAK`*~D-w51OuoAP6t_~~xdWwUyhV9^Q1;bQAklb+LPP` zV#pmpaAx3DwVmHoc7Mw3B8DyuxrH>N>xpa z8GEJ|aSF9{xgam+t{eJnW`UJfVm80w^ggR?Ld>h8n6 zI|*;ShZ%D(`bJ}9Wmq+JBshvhA;nnN8yQ^^WrYbA6Op*53K|c_e!z8~?5E=M zB_pMP&oXz%U^XzBhhPa{B((h5tMW6gjXvq07mM|SqQSLF&xhf`tMaGbFcP)0kQxFo zv5>~M579&Ny2t#`tfV)B@uf8(-c~y95DuN8T3h9UT}N@Ta?Ko@_V#=^BsKBI;3ThQ zht!=&xui8t>Q|1!T$Hr)ZLv}UezH3RYf-sQn2O4_V!!)vx05SPSKu1ow5J!#nO-8P zE!$Y-FF-ak@#LHN$`35}Z4%MU9<%^ncA0ei(zOYUA{|McGBA-Ajfi8Ioq|o+2le6k zaFwScL}(I56`Vm#XdXt1ng|PLJ4Er+7 zJyrWOuhd@Cl9c>Ozb;aP?Y<@C%3ra2YW8tnOQIdhU)g&GQ8UWp;pzCt2`hkC`W}jd zf7JfJ2`V@vT&N4e%Q5>8%swQCz=gkpjCAHsA8cA?<*=u-0^{z`laYOT&Y6S22CpdPU5ud%VmTTFF{x&WFup4H@y$sk+9R3nZjUU?@m-JGkc9;F3Lj1|ER9%A3_ ze5I`(;|mL9?O=HA#Hkxx1vxi~zkTy@*07uQ7&i#*mrhZe$Z6f~bZZf&G3fX(;8$_h z51Q%x#JWGqukbnE|5UTNgjeMfP%xaR@_u@|b)q+)@B*fRp-$ZW|0*aPo34M^hrS9x z^nnU+JN=IPLIDvs4?wg%%uKS`m2bI1<3#EUqsph`oi#N+V!DKf5jXLpl>&l3LQcCcp8Udi{MkFmJDBP#DgR6m~Ml`WW1tyT> zbto@}9M@tf(+BFhhJR{|a<4j=M)?j2noKx`N5>Giv?58b4_AIlw@q67MU1385!#F~ zIpmJUoMY?w;5?x~H0E@@#yzG~S-$F8gJX(4sgJ>UQ!3EQvp4!<%qhR&Oe*?1mF3Sp zP6Qa!?K;rmafS35cGuE}yi5J(bQg-?w2H98l2w@U;M7(XTAMb+>D=-R6`@uQJl4di z_We@O+eot2&7yb2Pj-sC4!Pq~bj?}}1jAXd!jD;Zn059@+3y@0Yx`TtB1Vhq*a&K9 zVC=OZVjX%}H_YU0|9!=F9o`?`$p(FPi8R^V5v}NB=*CX_W zl&D+}c|J|ib-l3V2T*KlD83N>7v0*UKl{JvmZRX_lL;gU2vpJ*11$jHn&J6xUotIB z1(yUsq_rRx&rYS9`phnuK+;LkB*~t*;Ye$7^UaXw?=V!}D!@>@pX6utsGCjok*TZA z=Hv|royczFBhzPF4s&+$pBqHWRA#Nl-KUJ2zLb;<8+lZ|g?b6y$_i5)eQVRQTxpsj z&5`=J%=Usx8Loiorh{W;E{z#&@$=xylZVhOY?$|LZKdS_P`Srw#Tg9q)WYNAD|S3a!qB zF!%-J!o|$fDg^tr#Mxd9bdy}@VY?k>%X;c1m{)2amN7%qS{bE2Vm z_+0#RnHd#ClEPe*@8hozW$U2XhMoFH>E#zvV4+{x*`XK7D_kQQdR6?0x)XSI<1T!44cSAW`p@LeKgeWQLw8iHp5xw zlhlRDm&62J^$$n$Z6Qr>4hPrdt45uTk=@`DRqIq-H))BZ{#NL;3Vn4Ob{$y3oIK*Hb?P1FmJv}ZSfR`QCB_+J=^%nn+hNI}g?h_~8Af)@+fa_0av9s*S8GJ+F>yn*#6t zv9qJrLxpeoyX8?=uE)0XQYKl99w^HQ8i8sLhWCH75AB-@Z)q8aL^tfM-h9Y-^66>v zpBY`gdBc8S5DM?*Ta~%e;lh?cL4D>RUzrgmb;uM`X|O@hH|6Dxa%9HO5F^Ekn|(++d*9EB04jzwmHLVWJ9rT;0GY{$2eMI zoa}K`;p*}Qm6LroX?#8gAM7F5;3qAg+~zb`>iqesN2V*1kE{-!RayMz`O3Q-jEDfa zT!UBHmpSOh_eGz3lOxIN4B+3OrT5cwdFS>`DEbt*$=8%7c_v#x%_idw7rJQjxW5Z9 zq|ix7x8)DL&JqBdx*FS-(3|A)2RS*C*=MA35WkA=TAun@RdcqR`ix?<(?mtXw$uVV zivmf8rf|G20%|=?;i)pW7}4YY1R!=BOGe+`Xsud=#i)Jd&VXrk0NJ#1Nm&wO6SQ1h zQtJGY^8BLeIbKaE!DY<+Af|@>?|1ll=*3jNeLYX%{_Gp$ z;=n=Zk?)Xg6KL)o^14n+?9^%P7iO|+c;%l|0%Cn1)J6vxp&7;CKg|}^SZoCjCjiWs z*X~#-fMF14dqyc*IqO4r2>9{;k~q~eT4;a{6$HeU1_XpKX^#Ofshi^$0OhN?v?M^$ z*!A~z^(PVRML1YP7#>u3FsURfl%!@f>Ymv#8CC|_bQcj+vo)Q%O>?ylo%TwFWwDwf zGGV06lJ;gr^}1YT%W_3-%P0D0WXbo#_M~Yd?9Y3Wmu`*aN(Fry0wD`xliM_K+PIOG#$iXobuTrLzea@uH zrYopI%1Gs{ls)#ug6Y>^Xax_ao-F)bEa(~61u%NY1$_GC9Ac|TGRRQrz-*sv zUY(_X;09f*;ThDGb5){k@4O(*FR>S6Ux9H>^JYW8O-gVg%_6ZkWp8~*2W?|-Z=TEQ z&rzL(za=O4=VBin@M@7}8J)7jb-eSU-=?;OY~&g;LZaW_clXL+;1xrQZ7)|tT^}D| zd`~}^w^eByloP5+DN!MJr|Qz%tL;4V&Dfa67z=a=@51OfL+O#^XGm;~8fL|y`j8`U zX2)GFR1>26FWZ&PInP8(xj0|8uc@)q?JRMB>u7GS#AdGrYIHjjsYj4txPS_3 z0nUn;I7U83)kor2pXVyXc}DH2s4c<49u4{KmG`I z`ii9WMQqbg7(?Iu)?z%3g3RA$4J}23EmBVZ>tk!@O@RV=s{td^l784I>=^NP`7!-E zN&-|I(D34``$eb@sr{5rM58lw+Fs6QJ$U5=>gq-ptQ3m zg|)=i*4n9$)&G~q#FU!xskq34UG>Vk+#f36@_`-oQ+~o~H4f^*uP1NT+4+>3A-05H zeQY-IyCO&Uf#qGUg&G%32CTF8YxCC#qC_W@fDQOa(2?5@l^+KX@4^~B^8{T%$Mjfj z8}q}^2BAkAw9hCxT1zGWT1JNKA7Db6ePsi7o217Y5FabwqTA3}mE!TDh6?$&hTa*w z8}(kWI(BfK!0FnAxE;*P9%U+Tj|i7AE#Yj$c`Fc>?sm~cOhb!JI+PZmII#*!mb_U} zf$(9l0mp*=MMC-1J{#YP9pR8`gF+jB8tf5RfYiQ-(9*t}7#Yqg)JVP#5~*9Tv>Pjd zQ_ifi5GkT^;vgVTEMg;L1U8$H`zUY)TG=lDJhTWq0a|_|m(otT#Ki%WRCRQY%=(SMp3HO&@z%p9dN zJa3I+eQ(X;odp6_?>{+;XUOlV(ijUIYTohq%4g0!&^Wn@x5sazKQoh5f{n)YPHwZ0<y z9wD5CzQ@__>X3e&m8VZN9l5H&J9zW$W@iv&ojplBrgPvjbx{hy(IGxt@6pRf5$B7O zW~O-AAg?Xih<4HaI@Iq*0U|h8kFv8fSJXn43xIF6sgr!2quhtjf>KD;?ZFpDJ8o)} zh;EZr;(a(jHh9(T34{t)nyr$1kuHTJd&`iGNo(p|7SKA5`TeOOktm3TSA}teldvq1 zsc08r?X*_8jS`OKAEf-d6iz?-86=nRLdZyTF{Erl$p_u1N z-`@z-o0a#2#;+2u0Ahn1&{P#I-b@dbK`kKdQ1*c-P;-xR|4eEtUJH}BH5K4Ea0kY( zK*U2(J&6UPd2jOpf_!(LS2GMIug`TEdC}kE zSS1(*)eKK(8@@2iBw?89812@4>p_N015+5O^5*6*vbR(I;qw(V4jc*?RSiBllr6DYlhjG3|7j~xy} zee0mbdn5hBE2l(X%!1!mY*Y)YvQPUU{wpnFBzV9w7UEMU>J)f$^Zg&MKOCrrND>q)aO~Bek>lz{hccwXWcRrP4uM<(3C6N>eE_SGd_lSz-0|y z9m&s}8uPa&7e9<;L0_ehh1P*h;4`Bt_2&oneQgm_4&9xFTj7sW57gO5iTm5~qiA4d z+w{tqceB|KYaVezmUc_Nb$Q7}I2PyV4TRbw=XpR&gy=vICZZeGoa0C|Ss9d}!-m?7 z0yNzkUO{0p30ipGRy9mljZsZ2<_8iO#|QGr7TW@0F`&)MK>rk$kT0OE0VxHpmOlW{ zHv*$IQ9v7PN(SZ!r~}fd1%La5zsY+TvWD>d1#)j0%Z*>^A4K!eO?)ZD3I+YL;tXV_ zZP)HSC=CUF1lTV18{_r`DRkQ7yPGy$VlRdxuJb$nYck?$bwaId%H#}lT#V0QDW|F- zN+)gmzsqbT{svMwnzj%T9g>g*q~}2H7vKDe>86N~QK3hTHCH6)a^{N*6AUBPO&Krv zSqYXW4{L*5)RIJjx11tEKLp5EUEQ`7DL*6*$kq%6bowC;bUjfnXW>zwSlpE<3V2}s7^T#n zchvCu=n=0;WB0;XvF?DYyDY3B%KPtDGK(!M--gIJKeoPVS6p&IqTeAc^_B$5b2^VN z#ub=zL3U`0KDG=+P}mu3$o(CAP|c8Jf>=^b!3|P|!vIQ_!+=`q8@cb=>_5NvAKt-P zpNHh&GsVMOtPS{6WuXOOmSBLsxW+2qv;BCvEu z9Z`Xz?08$^!>$DTfhsH32>)mfimm7+_o^;%8^@34!#$YUlaaAvt^?pDskA4JoE7b5 zb_inQ=f6{>$GXaa1e=+T6!H{r+$~nC0KOG}nH>r;{4hEUVfpQ)7keoVx$@u1Dp}rR zI{B;UZO(f5C;2><%a-R4oGyAU44VoRYNkF7omQEj?AXUj;#21(NgST2XPG@lVNo7W z-MdO1R0fO2EK3c6;)TE#Sj$XtLKD{P>Q5AF)|5;u^0z!nfo$PwMTy_?r4N>HcEizx zJ;JX1Vr4LX7)pz(MlfK>ORXYOYpxJer97t;hsgOV*4@e1!HK)rnr;|da7WQac1=;& zKLof?3w`Pb;IW0?skR`S>4$4N?~aFDs6M0p;aRp2xQh=@2U&qq;SIELmD3*~&vn8+ z5hcDtg?9zBs{Fz%s1+a4p$K(_|BN|G0X`=uEfUx^i#;f(=9h8 zV1E^BDVgfLAlWtr2J{!}LMiYD6ESah+T62&cQn9z%7SwM+7_M($lg>uz-2h0>2p%sg9-JMEhEw~9XpYBA9e`RDGv^wM1&_5?%^P6&+~%pqm)RDXjg@8 z?#e8Qc$)exwDyPnL)Qd$7S%?wK??_$#Y0>Yw0G?Av+*?RwF5ve?hS=Io=VdGTtK_n z23;AYzqF?S{m&7%K3M$Ojru^^r?LwVb>5Qc9(bf?O+juk6?GZY>%Q(Je#SBbbm_)%IuUBZ$DfVe8EoPsYp}lBgoX|C zQZl*jveU!1Z|MXuF}CZAGAHBu)25x5et~Vib4QEjyfsBGkhxh>9_Baxl-I%g*8=11 z=*-sukWp79u>C$q;XqjVL$vjP(MEKIBf`oXup(RYir@B^U`?M8o4@s=0~j_2a-SjW zogl8iQ!NHve>sBSJZQX;I(LALU2rh7G|m}w!o0TCZeV$4>-yGFpGteqJ(g25yqLVP zzEy{4=oPC;v*XJG?J_V>{=x{z0vPpkpnrS-*uYM(9NXS%Yp5+eK5CTB(*$cM8x`|B z^NCr&yuIU77RV84)k^PRf|gGl<+c*>fcG!0pY_yh6g|l3d8IXf`TwfqO&*BH7LnCfw7#r?4$8Hz(^g}Aw)O; zcRCt2o^qI&WY3NHG15^Pa@4d_nl$d;x0i$+hfUaH1{+-iWZS`I4euK1rpHio9|qPR z-2UUxwL=m1^(nC-*(Dsa*|qHO8%IXV_-AqmhM@iBhnU#QlevG+Xq9|}D7k0~`7&G) zfPxdQoUrm{H@N>XBaFr8up#~XXJSnUuyv84Z;~{%ZP3S!t+dZJ4*I3r%tV0Ph(pns za4+17^bbO)(>A=9UiYlXm3`@-49Ku2#kvqW9(hG+zCjmxgZFs_o*v+p)~s?<&Yamk zgTA1mYy=H%@jhYE@Pj&^c|UQvgv=;S^gzN@!8^d6!5-BPCelxWm3)N1!7K(*uqbmD zD1vxZOP?T;s8KEp)(Q*vv_)sD0KS;U&j2scH4X3Dc4AgA{HkMle3}!CMkFl~W)b4m zSMpElI7(a5oL*KyBd&>>kOSp-RWaCehPkjrnTEB?_4zwQ@1r`M)AE z*mBr&WHU_=-mx=VET!wm4e!&J(hnG(IM5Dz$>A@gm)_Q$#d3C#=yth=K;B|xAkN}C_1UT zI`g)ak?ueDg95?Z4a-=7ns>io0r|8@W&Nu->HTM+b16&TM2BVVzvSBOQD^dN^Ep|7 zu~(i*553EW$l=zTKEX_S*ka@ZZ>jPTKIGH-<+H{7nfXQFE}pTCgt~R9G9LU)x*`A> zh9oemk~AJUFDZ;%Vg-VfW^J8tVx^p8QY9J5U(SN+Nq{IdU2A(PvH9N&f{)f}+`p|v z9l5q<7l0e-^+8p7Xh6-He9Iv_M2R@y8HSD@G6op56g+iA`xm|w?=s~3cTL=qI%SwR z*WHP!T(hHKo~f!v8Y`^I7bf0!8n9r2U)L-I@VE)_oT__*`=qO10Ub`OSB>xnvAi=h z`8sAE!d3LHuG4m3ewKkJirhKlIm(2%{IN94uwfW{r5XFEXYf*8_MCyb+MTgvKh}dy zd-xnIlp4T-J7dw+nZY(Sl8oCM;d1-89`7&L+sXBlO6aE(T3zrM(9I~RWxg8kSzS0;GL`rR%?A) z4)e>nTF4$q9);1OVX!8YvB}<)@)U{nLTftah4LVu({_ejZl*MA)zTaAo;Tek@7Ek| z9xqdT-w(rlzcPn*h;bZgLydTVkynTlZAW8M41|J6H^4MdQ~=z#0-zjgbs|(jd!yRX z{6jKp-J}D>;Z+dPd&r1hieC{2X$-umeI<8k5YAqzg=Q*Vvd1%G_DFiMd(GiLvl;lq z=)|FWQU+cci9JLEYD2Rt!f);2c7b;?h&`yo&<0*y{x`cF{@;ceJJ1L3j;|u9^pxzJ zI_e7#38rIpQwb*u5&+_q6I1-OboJ6K&iIBW>uq%!%;%#1U~_lVNx^diX1iJ8Q-KAh zENH0og8(TgVB{d0rS%e-iBw9vO04G}4dxH1-YET9Go=-PdABmdc#BV`)`=3H%4K3X zLS#TzXF2PM(X&ZFCWBw>;5`qlipPxo{h$uX&iI1SuDE4oGNDG14gjz}l2V(@_g{O} zYIvdhu#WX;vOb|dD{_&H%p>1g`Bk*H*9=>rJnuAIn8qv_?awP$W?Y$Mv#=&<^86+9 zxqF^WytL-*qU|S$-|?)TwXV$^?&Bp$itZ2b5Z&!cT!8*5XwecC)h}aYxxiQ%kNxa> zBr0X#oXBEys^*VA0PxvCp0HPpa5@tbB-qnwd}zwwUMjvLn&2OTM-eu6%am|Jo(H)5 zo}&{Ha2)sj6I|0}HclAosi_n_3=CG_F5Meouh|d5BD<{*9=Qt)jxkad!dmFu_)BZ` zil{qR8_v3YA3!raVajW@iseJY$=4@`nV1-yGiHgxw{i;|2k;3~L&@}h5us=EDcWDb zGG&;E$WHU(AF9Ce3D~iG#r3J#gMTgXmrYp$w<#K)(q4Cr%#?pipolcoI_)gpRS%e? zJ}tYMPNE&zWO)8q<5HkFX<)3_9bDEZWn7?2&Pr9IA~t-VaZc&iyKN8VDm5<+?{EX9 z`1p8mFdypBHhf1J%V-T@W4V$D6ue?js8AhvMJ}N;OV!o83{Y(n z?L@aTH&m=dv6}5Hck1oG^bEXD4BYhvfO%f>A7Hy#RWonb&cahge3Q`2tE%?haoeG`a6j#{Lw}zBIQhJo=$eXRF}d`RCHHDD|Oe3^1AqyViFr4WGVB z=i-5Ih+Ie#Xe9PSjywkqt_?F-X_CS_OSfua9J;Mly@jD;LYLuoNzgpONJg%Geakdb zG$+Ft7sK!;xN6)}kR-IcH@Oc`IHcdDh5b8IXo199MnmuP$dM&tlU!;fABP_#cW}WI5f_m zz>#(2jFyDae(>^t3!M&`ctyTeMb6+Hdpxrofzh;YM=q|$=#QCvV;mtQswAY6cmw)^{qcKr~5&BgW|8_okMf0^nJQK9z0?^GKzn* zc&-ZMZ1RLU*z>O5zg?R z@e2`PPZAC8VL91%{nRi=f?Ot_oLHtW5?FyC)T(cq*VX$e;<_YF$g* zSNrt2OgNDCzMbmB97%hNhhzE?%OkZU`Yy_;5lA4oqSF8K3Vvd|#lq zMBUK2$O>S{K#K%ey%^i_44A%hSVlk;?n<)TfcW)9e+w{XC{qw~{>2y$j&9naPL#V} z$~5AyTu{r+Xs%`hGGbuZgXA<9mIaN)c@CZ2XMY2%=~Q3x#RiI9+!j>} z01vlG>YKE!kw^=AV^Sksf(9gQSLs@CCVG@Exrs5@TL}#prtJEj>nL*@lF4iIyp(XcCu_d1`wmWr~&Beq>S2m`9XijuG*Yk|vmgkKi$z)AeBf zS{Q6*W{#3wBI(-NOavIjywU&lu@9QpHvBHALg{ny--H>r)UG~+{fMmwC&DH|aWz4-} zg;@F8a^GJ!a;r^1uhjB_}+duC0wtw@T4xT|~vSVv)_ z`Eq(H(blz?)+VWYx+~aK*NnD+icMeAplo!PSR=NI2wF@)b_mwGyfrK_oYsqG_WklD zz>He!9){{wc&Ox6dg!FJXdl`OP_|F)Rke@pMLnzT;pzcAjzyV!O@7NLV>>7~-pm%^ z_LrXG__X<0ou*)!0?&sRMs2NaoR}CNS|Yl)j%Vd7g95Qzczt|aP4s$=#PtW}Me>Bh zfXanf){9Pmx^U|=LY3-XDEQk?_))b?zZ{Cz_Or>0e!rc#rRO_(7KQQvus-w}h2&9X zaywGUBr0LChWSZ0r@MeVUO~en5S7GwUw^mOoOKz$ciY{ld=O_-E^}e32)x>aLGR7h z5>Gqa5@@Ibc}HS$XZ96xs%V1es=n)m`pQh&`v7sT2fN#r=M!#(ZpLbUA~&k$e-A@z z5ba-3^MW^cLsytlzsw2_K+IQ${`HufVubrPD~A#=q<4!1`OzGae)RWN`ssM@6S2Fy zqvLi?o~sMmBrWZe`4hxr@eX}8m~%f#a`rC(JBztPKE-X0|1s})z=MTJL>EJ_N)(^5Nv#h< z1&lXn0t&2#$M-wl|1CPCRLTAhUz%ILcx5YD&oJ0Qt>Xj#|DFFR=P84){Xw?z{r(Sw zb9eT4LgXwmAS%J;2ib-&VwU%~ejq881L_5gY5~7j2n9t1DXt=oDg=hK6HA+UB;FR^ zQ91fooJGgO2*v4{E+i`b^SE6qt^qJ3p~MD*gL;#0DK&;&o2@n z2I(%n$DIboBsa=U+ECgT4?cr|@jDvdUA6$3-6o|EAb%AKI8L_uz5KikzRbM!#Cp8} zQ?DQw(IHb2&Duiw(sj~D7$E3Y?lVgL0R1%02y#{7gd~$saUMm_RgGN9a7v%#hB2ty z2w$6B%~AbX?X+3j1>ZRb^%(BMP z*;(yAl7frq$yMhybf=-P;QV0C>D1?is9bnJM1gIIL-8!^bg$F zVAYp94LFTZ96PpvQtA7j3c<(_u{B%t<>xQ5+nV_PZ#A4Zb0SgiK(x2YTaLYlessYc zV9^sXF8>OhN#@5tQ#rkp*58zalhz+7jSRAZ4)=d4@I|x&ff_wR4GIrpVN4?!g8BG! z0rm*mA=sywqAi1DXrAS;seJt3uDmpKpy>O4PIopK==-Rb*9EptZzzw@C_Iz0xyQ2X zc^EEXkSKFqv%|=%4yYt#0n$_g*Zn-G;?m%h1;dv>g78Ava#R8=n8u=zj(V{9V+2m6 zWskqcpkgUWM{{AAYCtK=1i(#VMHXqEq<@V;#?~W|5C(9o`KMaO2Ex>br#ImdcmOd~ zfl7=MsF03=u!<{!uklZ1;1PfUF(F%;{^~W zOGUHB`jdye?tIhrZg#lv?6J*Nn7~f)DRASN{qlZ!!qatsgRTc&3-%p<7aA!h~?fhY4nPra0IF)mTg@KcTc=-VfSYBb$a&2cf z(h_rZYxRG~GH0|jReFn0pg2QDnf$_ikJrHqbTl;F*%g3y@W5pBu-_R?w%HJi7wSSv z#yyjbB>DaRyPXOVz>^5YPHL9Mu~iStLvK)4;MvE)l2hM^vt`A1Iho~OqHob`V0xywm1+@}dEhy|ZKuwX zzML3XHRpJ{LFk&btOV9Bs_!SuER#-q$yo2HXW*#Z0^XzmSZ3WcDsG%4oMLjn!K>e@ z*1o4a+XE(}g2~EhY+4M{r9IiMSTiz){aX|GgsS=0VLLzwiKJ8ULL#zqmYD6tgHTHj zIrE8WRPFXfcXFDr7@?>q6YOvlya)2^lX4lX< zAr44nz!8U(X+fqi7%PSUq8;y7LxqYRaGlL0$vBAvyl?8u+f+tuS8jsyd8@P-mb|fu zuI)M9$i~gTrK*l@o@#U5K%9lrE050O)5zzQlu<;BLu4C^W2#ZsP}IpxT(jPL7}Bm) zkcgmhHMJJz6S37pKu&#kxQ;^E)k%h`-g0fQxpmYnUMs`wXOOJdw5L7O;sq;vVejM( z>(qMya$}pKB)Zfsku-I(h)8coF({EWdf0k+;6VTtU=+dvG z$md?|q#m^kG{*D&Qk(fiC0;m|X$k##*lS8XiVl%BpR6F?|H+5xe&UIhDHKhln+Kzk z;pYT-6KiZnOVpJ7apU%m5%~}p!oMR&1sqFbj3{6N6z;wTflW&T4-{^Z$ zGH0M20vc8B1Zjsb+k`;@91RdcYg}H15u7pnhhrHsAh}CAiCrB{TEm|;^ie@Wqe@qx zIjYXjj6fX8o<4gVw{)H!htjEtEc2+(DOmJ=Cg>|$Y}BC6-wO@tK4(G`-J~nZj@A>| z&Tip^CpWBBqc3rZq+qJR7f@t~sXM#?lRT4e03J2H26a*?ky+(DtqR~UbBhZhVphMx zVdDz10yTfm#AbHDhC8grR1OUfhx-MB`AD_KgeOCPM+>S} zIP56Q=<;mAZJg`l9L>P57VZ?q2Z)%m0$cGvAm^m-k)wr&9iO1~>9~FW#OqedK+IQ| zCy!nKnWN(`As+OuEFA;cham#pyDi4F?uH{p+h&QN4LPxYsRgLpW(_ruPo9&azR8P0 zB{EqxRrqIUxZtHh0Zv<)a>$6Z>K)-WLdHN|9*4EeY6E<@==skM+GIc*lZ}_qkE-(* z50$Hmb4B}PfoQS7n4z-twiJZ00OxbJL86q1&3&cHylxGO{Uq{8ALLbxf+l?Lz z=zq7swaiW~2LUpbH&rx7M!4}H+VEl-fCjpDuD9XuKgTByvM2iXj}F-P-l&AY!8o3) zq93<9fNk!;$0K>rBgAF-Wn*KQ)ur1eL$$KBhO}XkZ4|{JiJ_H@r7<|T#obU9H;6*O z!r?-ZvUt0-L7rRJu)>FmexX_y!E(r<1&1REQH0Wsiv!4Y3PA#uuLzpn2-mU+wj&2C zlp4#CQcH9uPt4I#nuIZ>gBm>4atZBWSz-OomaQO~Wox)&*3~nSJT<9E9heRL7*M1Y z+}uOG9D-@sBaYltx24U1;*6on^+eQ%)AVT_ zvS>Y-1*j}0ThfJzJN}%q)$jdU`HE`R-Afta!Ly}0pFcb&+N1UV-unjruc+3?EM`KM zg~)%X{RyuYm_SesNS5Ki{};G_C7`K6{YN9Ygi5Hj76NFg{YWEUvP@&W3`aY3J8}S^TefZmhR7EauUvhI2d~xweIPefnteQSzQc-lsQFj% zzLC*KeE{SHPwEg9zG}y97%)EXm0u4rBIgi&p68?QGMdwl%Pgb@Jjp5&$MRl&H?BebHfB^AH0@n5k={H?6{A zr6AD-N3jaQTpZe5M{r4+2=NTVvgyncZi8u^R!bFagS;!mz49T;D)Ou<>jW!lhSysD(A+#E_i4u3Hea8!Mq$*lG-d9 zMt9URy;};aKRPl5)ZmW)PkF8hcYdzvlz)*2QRz;1BQ}>X5)b*;J1z)PI&}tD@HJ^c z6;1F509CCcUNQ@sUaZpis=fgGMH;VJ9bg8=Jz!Jjj!F#kS#nkG4tqdF#z1!HBY>6QWro}y$-R^J%dU(C)1{srp%NJ+dZFvbs0jiGI_JsBNn;#m2|u(WEXzET|B;E zT!KgrqmFK1*Az}LT?67%9$Anl+by0{wI&-J_g(Y6619fExz7%k3tfilHoN+oAw)}u zOZv+{rm`Mvi>2$@B_y|40)TAEMt8%WI9E4|001kvkB@V@wOcm_=l|EOLEsua2&SIJ!Tf~fvaXIDn)Wt^3Rgy7N#5a+ZpWZ zGFUhvv5KG;rG}stg

      Z*Xr|i5rG6t4DuAio8BN?_7GyYV!0e*&@$X75^(rd`Y9}) zad#}B`0==N`baR|)S*_0pXZLtjbg*AN_W4Y>5qu;1V0+|dpju^qoXuwy z8Zvv$sV86mwRxmL-ysPI2r8j&{wFEyBntF@S;$JJP)oo+q%^bt_>FV1xCvp-Z~!eg zWEG69gyJlgG~Lifi#+W^A`3z6rpDZ=O-;HX-PvWdaEJwC>EFd^3~Ba=H*O!lJ70@C zZwcu9R5T`z65zZ4p?_e~rL@i%bEa(;PK0DWWI1&=^lW^|9=`y5zQB4XUdbYD*vSvP zFn}_xMDUX}9f0hh&+;+0!*&tqL;$x?bP)ncbvOc8n3(C2ZUg4@TjNj`BI2G9YW495GB}PR>dB^o-92@gaYjXpgd8Mk&t>s6T|Fk@G*AEx4{wxamQDs!T3{QH74oLpTh(5EWf3xGsZP7hi4TngyfvCK(14BGIw6A$*LTWG|=XC%>P=rz$!bj3OH|^TC;pR zZ%lWq9;X;@r6X)J0FCprS8Xs`p=_Ys))PwaTKWr{8CF>uWJIS$0yAP&I^Pp#ja%ME ze%hE<@pm6vQOV1P>CknUZ?EM1_{p7mY~9czD8Fyi(@Hi5-G#V7BOgFu$y;;8w4j-n zYpOA!+?jW;ScRw%os0zh?(TwHm<6Zi&aN2IUI!zBdBHfqVE2kVi*jRjVRIK;2G`0m^9Xx4L@M$_V|kWRC_L)1z> zmufWzo5Xvq-=e8jkQxBhY!nii0cC5@;;&`<(B)qwdL+27fhj#s+Lz9 ztQ8+&@P7I4D=`2WC++AM2Q~9y8u^1X>r?jnYUI~SfcB>!87N1N1b1+vyMOP$5p&N_ z{e3YOK4B>jG1+QD5(Cd~EKSnQAyp^?$Q~l5gjdKuKUNALn(e=DCm{J?ar>>%gwY#q z;dvlDr^BjmT_6RIXCy@umYw2JS&s%Nh@HaU8G-a4XjlMX91AYE*&Ty%sFV3=h@ybR z4g~k{*mUE^F?N!N2nYaTCCY7uGS*NkwrM!ELzl+uTR=gqV?7ny3#V@4ZHCSUY3xd? z8-|#6>>u{u>=R*y;!(a9j{5!6u6*?5IHlL|>FB|i$>lh~t7`|SC#6Qm7-9ydfn0** ziJ_v)SD*kdSMW2}@e{|#Zl&8*sU^+?r}K z3W8%g7HKWAZ69aa2-@?uTWr7!f_3{1n9}}vMt8nTd+2Z6ZSeINs8J1g$Nr#8gxI;} z8*Bgp-75kY72*cYVBtu)0lI=LC_a%U@*UER_HEu6UfJ_f$Z5G>t8nOq3*oeqLgk;OL zNrq`%SJOtRa(;jLSpqobl5$;bOdL^$l4;p{Ir|td=p0v2tc@O3x1pelG z<^2w{c9>ll!%#SM=GfF}TQwZAVUi_kaj1LVD}K37XZfBEav^Fu$svgjep6Qlk%(+E zidMwJBv1XY9EoplD4b!^p2Ib;@B&%#ToH$!^@Ns?`BN>Skl^t)t6R?T0;VRjmX{jT~pWYia!9ENrVe&6AK#Sc(D_2>dlUOydc@~<8 zAQUYz0tHGy;&!LL(k1UQ{)(vEe`XI_KVN{9@r(IR!3*3dvq39YT_2k8Zid6j^w`U3 zEp6@LVP?hd*L-TqtgkRGtpsG0xVShWE|1Cc;Kar6&? zC|L8NNj37P2VQRoPLn&7@hHkAOY~31U|wD^@g}V}JI}O6Q*jDy3}Mx25XB(l+*ZncC`D@dZ7cSkL8y!;Mgfr*jnB&C((#ORy&SAa#;$^5?3@n1D8HZ;N zXQL^9pe#@I%pFucamZjAGvsJAN(&D{NttwRGt{+XziX_IMfRy~PbV;&c{x z&NjF>iTLaDE2l=WpG$V#`)gh4RCe*K#;Z|gHVV}h4JDX`%iBf>XfZO1^P{cuy)>zu zJ%y_Q{tyR(2!t#S+^~i?$T$fC(tDUf8CsSY;5xV-c{OLl$v(n7jd8MiCc{kELsxs2iLzBtYJ`FS~G{q zxlK-#^^OA5DUL>?f3s!H9wim&f0yz7#nTliFV`L;r!l+00wSPwTD+%&n5U=hwZ$b>g313NTl}3J#OW50D}1E147OLXU~#r`Uu2 zEC@-L9yDr|UB+2_xg8MuOZJACJ##zMlRF%6Q<`=4<#$W>su)S~MX@2U^-Nbtn%5H1 zt4JeTssEB)>$Bmi-in0gYYi@AjW$IS0+G?O6~-C-yfwCqwmt?cuthd5BtVYXhueQf zP>p~2hX|*M&~Ht{kzGvDZ{P~1HUMVP5Q>6L*EIYlWB-)M-c(6}>$iV6q3ucQ#_(yM z$-3B>H3-kJW|&jJa|SP@8<_B@+#ki$5ci-W2I2c(SMY?}c9fBC+^NMp7YSeg^%wj< z$3+pQa6$f$K}MD!3*ZI(A5ZN2FKw42ZDT0`uNtTywY_gR(Xj9>AqlEXtf;#|eWMvi z*LB)e-YenD(avZV1z~?u_GnJcv>H<@DtAm#L|n?fb(@3;r)thYjCo1rUo! zmjssT0&{{e2@s`PXX%SngHh#9&#VZ`F+{cj`LP7n3NnwSrYb=Ag}Pju_3BcQcMe$a zBCZ(wmrih8o%_ojlaS+B1rm1&4E4s7j27=f2Azg%VkeZ_gf^!ne3S%W!h*s0rt8t> z-(l2^yI|Rb;Hu-;QB<%@xeJ3MiD_&gPV{w2<%7$Y&w}n#+hc}k@K@bD^;fbM9XhMk zQc83wZKNFoNh_eT>~I|o(~}!f;560b)M|XHwDD9478`dI*Ki1ZZdmo?Cg`6Tciny% z-;(jyxPG0I&D~vooU^u(I&VMjh>@8;@2qR_62HzLuS8my;qn$*>SrObry>4~nZ$C2 z2$*`y)@+eKFt~p(j*ZT&_M2|E!{F5ZxUa^CGnO2fqO|}RW*9sw!HrYdr`MP#@p@QK zAENkFg6EpCc`m!59DHE#Ia%KT=BeiRUsmll4rdQXOXmT&diP9jEmuOizD!V$+QnzE zMPmFSn6S7YU3H5xfTi2Xvxa%^IfWk93g2Fs z9paAtfe#lRf14Wu58M5}{%+yO$S%;2RFeUnFda*tVC9bsxKP{>MfqArt0ayXY)uVp zE`(zR^6J}+*UumUOV@I+*CcNw>lzbuK(*!|o{-Vaiu=bCoqDDAU}i)o!;Dpj8C>VJRbEJ) z+^({;pt&Sovuwq>v^kDl>?78M)t*HB7;k{}j2eucuzkCN80}F3o074vAFFzy#W_G} zpl-K3-tMACt2Tx-y~5hmo~IeC-Vzy~JAt4vhrdV*_-r>|y)=uw51Tx`Vka^rX5zCoeynpl;itUzT1RY9p)i~7itZbLJ+j!JnwB5`$<7vb+Ws=)z zHQC<@1G%!>KpY28f32Lws@+2GZ~j>oH~Wes0Mq73yj-%iG{bi;>~46FMaY~_1NtbW z-PFmef?q^KcoBon1JB|r76jI92f?RIUE8Z+e-@hX&*8n%(mXs8r?t`bYfBbg+7DPR zjh!ykpsFN%@uDbRMP>mzYIPq*r}`h>^+O_dg$4E!3o3{L1{_9dxr%efo)?$QA0^!` zz=rL*Su7&V@Lz-4F#%6-_|kWwKPuaTXBPCUU6xp$S&&Iil2hrD^~UYDoq$HgzeQa0 zM>>c%H-9Jw@V+5$@cX|>ZSUE25sR*=1Z6OxgM>Xw((ri|uKI52s%+=ilR*e9HZ!q5yX8OEeas4V;h(7%^R=oDb0^JBxkQ!lzA#1p|hWIJH6yIP=B)^pJ zFa^0VyxJn{B|77<;&cgjJkD_>XL+DyotcGMBNUPcFth~Ybws0eijEzI@_KfW02>80 zM!*TeT9d`*$3h4hp~MorViMUX#bqusT)&?_18_nH5iy|rgfne$Tlm$(M6XXro}Vtz zqt`N#*{=nkft@T9<+%Jfj&Z?;tbx!o>7D2993b&$TLmrQ6k5EJX&A9aNatkC;&O{U zL|dW22-?q7LUlPJHUcANU&{^$h}2koXvhAyTi!@jp(*oI=5Bt@3eo>`#dJ%gfFdQG z|7`Aad#{skQJe?~CkjU%xo zW8NK1UxNpTSnUg)OmF^7yZx0l;{$x(BKDH8LDwo!>QB;eY_d-9U`vs8Sr?f8?CYZ< z>?!g>wLxZ@Ske(}Msy}M$S(qT{qvgLN$?cA^vI-a@sGZB4N^VMBx{(XMnyWtCQ6jy zOY|uvav|A>Q}wAqe1s1|Q^%HnMJQdC6 zEhZr*y7+H}$#D81#j zGoMGR%eA{U#Z-*}e%GTVi$LwGercfgY*uhB3^Om7G=r}j>X8Sy z+F>UxtJhR+X+ts~^iiLn|8H>uKV^ah{fP?nA^!Ts^Pi8-KMW7>?uN32`@LzLux1So z;RlM0A}S+Z?}2pdXSkl~56TA3hJkh0SIcD!&c^Iu$_ClJp=opRu3-7g4=vHs0?Z0Y zF;^v~sVW3eaPs&lce+=)b2E8q8?UoEi2r#s{o-@;;?gtI@xHVI_{8>=l@6H&ZHi3( zBn(7MK!7Lw6~zdMxr^U52}RFJ0g(pC3o?ZoK#D@N^uq`YLkPwEHb@!>9uO`=z+xER zV1x`2R1YE#aHk4HlYcx8RJ|+5$w~

      4N{GQ&6YCFZKT1N+;=a3Z*wK(DQfcDYXtt7z@b;hk#X?Lr!(^D!=jaMRE=;hvv5l<1lR*`&{Ag(UPDw(smh(Wx85(nuwC+C3i3n}f(mzTMJD_}jclK1Y)FH$@4t z63@xd<|M|cc{G|2nlF-r8L#&kd)w%huKqZOY}_x#FQPqUpdxw9vDCrDUfi8J02IKP zuD1Gwfp#XqN=G>W!CXVy1-ep8U8^8DtEygCtU-CC(OeR}+R`t$-)8oPa52+@wn!bm zVzJ`SQ5xr8;s@w(aD{9G#Uwk!y4ylil1xH1HTtQLwi+bCin~|t>qe|}>?9W1_5=wh z8fFFa56Skfpo^Dwi=!hTWgaW4uid%lSIRH0!;H1H!KZg06oH6_uM?XPPLx z$oXDq9aogmz&F|cfh4@&5Nl{J0s=<1z!<4$FIhr-Z&?C#fYnbTF~|x6oAXBUve*Td zgdayhyfc8x0i|glV>#SS0YD#Jppn_zCES2WN`ud{3fzjYp>rr`?NJ9^cLjuQcp){~ zUe!9&az|oe@Mi7~btG2%Ms(OWPU<(I&Z&;`1*-TBMw1~rw3AU%%k8b5_E*~8;sHh# z#Q+{s(<7P$t)r#X1U^(LcF>~!%exZUY?hRlzvgweOQ5NQ$0>lK8IYG^y&-G=OT=mg zp%VmgXWcsx`LT-JyN^;?CO4RMCRbA*Sc=YYiJ;?Ou#;1i`gk+~KH?Gw#lDeR-6iBb zs-2rfkf6Bd=<7P?*@Uow-o~1=wIrroc)h=WH$mSFc#K}@q1N*|>hl#SmTB5KM?F~$ z@q{}7s407S2{GnM6yT{Nvqy%$iq4+p+L!JuzkCco4x@wNP0QSxwxo(^AJJRV%DTSE z8Pv|a^Ra@g#3vLtHop~HY!zBBAiVB=obio-G0xePU<>B}Nj+#XJ|ah1p0lTL$1yyJ z{;2%@n`|(Jo$M<}YJcZd&?^WKPF>y8f0)oh3GJqk8Q~Dd4RGtpDO2;pcimI@!1(k* zt1O;u564Av)e}hz{QVWPUJT;@ouwu5=aVsBK|sa z<0pVnca_uA;{%d96dw)h$QeIMElui~&fjtlG^_u=!65yR~8{rSWWcE$1H8s$xp+ zU}<_Y*zi_lWYxiJO{Iq`^D0M*Ph!Wg;_QM-_3;H1E@!u@9dufOnF z5}A1obGr zUj47%`5hP((l>(dJ`gWsBPPHDY_<%8loI5Wg7+qoA-jKnFydgvI0qqlIb4p?&3R+4 z(U{t>3aEPU3W*07-&YGsT<`v%@8<`Ry$roY0!poDf_U*JEat0ttJ8VL>Hl9_R{<5r zvTX?xB)Ge~yGwxJ?ry<@yEC}^;O-Wj1b26L4Hi7O1PJ+)ym#*l_x@(FW@z?4b*B1M zbxqe10t{D9>lwSYgsPnogXu)>v$Udgf7m_?ZVGIc8z?#>$fHNK9*h|(bmFXg=$DH< zRY)1Za**Nnf%a^~Qbc6**j(v^N-0aH`yT065(#=5~Vuuwo+bXhv;(lBFnVWn=Dkf1I048U4d68);l zP%^#}(;QSa$5_C04V*%={rJie(HN@eoR?*Da~k0RqV8jE+=1z6LmMAF(pC4l;r9zT zQIq*Hr8=p@B@2#^#qI+i5ia2q@dLSNbv0*hvry6)MIVM6%z17{5V1P6mv;#JKfA1Q z_J0U$t&(aA?xJ^UPdm_4kpnz8d`O^Z^MUVG?6{8T{2Kha@i@4SioUgj{W+6lOiYh* z4Wlrb`!>Xw2$?*F+^Z8y&wfB+_M{}b_9oTt5kq-YUIzPXIz_be;-O_(DM@%@$ZYED zRICcF?R(Acwh$dn#hOwh*im>V|Fnv>bJ!M8I9tNM*1SH~)AX2yiP$s%#08RXY{-VkMpneLZC zD0w8fV7)kX(m^n@=`#Q-*KX%q3=$tltSyCSttqXDdU{pP0P<0zFmKCS)@d92ClOLG5L( zcbg>b(55QdtRiWH8)VI)_r-SQM5}gm9BRW|#mL691TFod>-o~vUP-rbA_cMNwE8g1 zt}|rZ5+CI)$qzg@QS(z{=up1@oMsUV8fYwp5BQW33>wE(e)4*YEEz z&&gN-RU3s_MReb93)4!x_|vXfvj(lBVZV(>9x8{Li%D&+3O3fq*hS?Awoe+ zzOTr_x9Wcmlo~|uHz3G!(lF{xj`~bEeG}Nf_Wg15{f(bkB83SvSzb=(12f%w`2aAr z9&l+=Ofh;XNhiK&DpGZBZ?!%Z6f4pN98XSJ!0I&@P6Cu^tS~hn^y}Wra!WkE%C8+; z9n*IVm+9FB7d~0-{F8{eAH_F9uqfo@P{iarGPI=t2*=46_m``XW-Zxuibm;xtf!EpL zxoO(%MvJ{74U>}=cTf{KXcxKzm<^WaRRB-`8y%;q)?HM@DNcj=!`IsBG%wQEB*ySe z*pxfv0}eazML!9DbRuH9#uzo}99u-}3s)Jk&{7~1!T9zSte~R_LPAl32{}N8 z&0Nx(oWh^P^Uj{?F~n$Q57`f8jbzw8GXD$GyJXU4RKE6;HsUXKbMKmorj~$`G!*bj9=YwAj$@Bo`Q#!+z;TIOn-io!ER=@e3U?TI!kNqtyKe9Oy^cs|&L2J|yBQSm<}2>QRhcA|? zR?8K2&1cnzs?Y4>?@ZE6MhDeDY*TaQ=M0sml$NF;z81OD%Tdxh#VHill;j8<$R5#o z?C*Qk0CT1AfWL4@S&$>rtIB4@9k+rie^g25K$idRfct|^f%95wdK0qDqnt^sYR6b) z8;XWLfNEw0Y&B^8!Q?zNL?_VAOS1GwY)0#+*}TKy>Jgj5%Q2@f19V2O`@#5v3J&ky zQVZ!%Yu!oTy7%18>Y^BVT~Y@fkpm6(elCW3MVzn65<_}NXokC({UqEVfF?9^LZdpQ z`pV514G(hxe(qteIpTst*Ic!2T7{9q#J9hKE%F>bkJo#+c|J149I!vltG{n*=lD~{ z7HI{_g z*lh7tZ&ihO!NU?RVAqWTR-eck`M)hzeCBrku>vltFcr4mRWhD`+~QAYN29T$)Y58TMUUQWy7fd177XR7 zSno5K;_k8I+~DELCByGp!%G5uQ`>;nf4Qa z;p=UvMjOeYMbl(lMBvxj5NLR&NmVyDU(;p0z1a$6Q<~n9aim&J>F1%HNtOq+F=OE zLj*FkmC#M%Aq-ZduFE6Z6swQV&0N^NW0s#}l2g#gb5&-tOu=9%=Af|&%#D;@d2I=+ z4Lv4GK6dC+YUV&>tq_7Z%#2}p?U@>&pZqqx44d-NoeiF>2>?HoCE2?xPp)jCFl$j1 zUWiDazL{$Vgg*ElP)jt$i5QRifoU0s0eY(Q{q;fa*<3ecKNWcip?^R7aIeL7gS&1~ z=WEl;N^v4e%5P$keqtZ(Wz(UVU1L95k>XIrCCC*bG_?*_feVz*LDO{;v5kuX`yzkyyi@l)6#Vv{+4`S-8Gz%qTlv5KYaQl0Lap@Tt$spc`@Z7^flwIPdP&Ss!ksp>mv1=8&{Vp}e zKEn)J>)Ql+x${6|c@p^}W7pm-fHWK#-`!V|`}R`e?Xs!>dbEZ@p9BAC(g2@Vs5%+F zn>m~sLL^GleoawbVrHkl;n0sa>3N;DTzV*=eZgbE`&l@f!6%vx&$|;mRnS6-q0`7_ z)$x<8MyrR%ke`2|KLx#&`&i8~&{zyoeI9|Ht48#(@2CR>O#r!Wr_f&sV1hlg zWAcr2k){NFNc{2K`_!W!3R4sg^o=-#?gPvt--4(!whRAv1rW{QCp1jW2B*8yJfrn@>J1sw-~RFD?B|T2XJBswJ6#|I5~8l zAZ=oN_YbUoiUCzaMYIQi4)Gjy{FsS)OcuouO}jZ=KC;st0njLd?4hCYSp#x)#))SV zuX=aoB;8wLi)nJhYR_I8uc4f;8YIYgn?TzytiM;09!sFxtIDR`j3UUjo(BDmli%tb z4+aVsHr{+YP821ooa~TUz;f>f<5Ugf-ol!z_)jVW@9941pI|?!cbXY8)`^AFKkZ9& zUS*wSu0B6sU19k_e6lf7s*e|Ch#llSa({0$C=wXwwI#*mDK*5NJ_uQl7p2?tVG{Ul z>$}O9a-{cP08Yg-Z$f&6Fh^!D*e%6N^d+g=uxOptgiukaBF_q))fTag4lYMsnG}u9 zEBc1qvhvP&)(u%v91Ho#U2ar8LKV})m>^QfFbI62+TNlr;I*?|NO5<>8J7^zD8%rM zqls+1VIs!K8_?c^kEuZ&LeB`M%*(vjaLyUCWGy+}1~{03+!0CQLjBv2A3ZntWzJ~g zXom23>S|<`Ea|^`EKEz-c>7A1{zOVQybE;VaNhKMnBy{Iwh@p+!Mue0ctLCZtx*S4 zTkIsy+9;={am<>3rSlXYVPehb9k-_MY0%2{Ru#KUOn`Z6n1y?J=f)A~>-v#Sb;O4{ z!vhRIdq5yrC`0Y|w`aeLXaWdMAtzob3iKr=O0b5He#6p=G2yhjdShi|QmwVBE=Cx4RMV|W(-Z{N71Eq3yveEOC^jBP zPf_-)OYy7&6@qcl;usL>k!#2pI?u4FNafV9-XY^$Wk6us`{&Kueq0k_9b_w3pq&$; z-?pMtNDLTNMpHr-a7&QtVSWpPd=VT*Ed>*lf&^I`H#0*ES1F=Z(@L)5;LIv2M>)cD z&%{_5A(8Y(s4EH2;0>4U+5wZMt3t6zkRl1=n)Bvr!*3iUKOZ3-grssblrg9>%fCFzBLOePZvt}Km5&Bc0!1-DPH z)C~`q>nBx8cc>&yHa^MO33o{nm|DwEX}9{(ZDksbw5?TUwT_*WD_W#E zSxVgUq#HjdiBEQa0on?vD zQJTug{;^*|!aR?@3O2V+oNOaz@-WYdC2dIVGGheK=>-W+P^Z|`y4UO|r;V|4$Km-A zp)@A3kJtxl^*{zkBmi3EBV_E*9XrJ(3N^o=BF0LR1s3II3d*A*Ye6fFX7fP9%;^{saht)MVk&NM|wu7-C%4ezKb>{Kk9U*~o}kAZ_!zkvfANkn#kbFOnT zzd6m;;%8ozm@qrRYJC9&cc=+t;NOA$0x|EK&EoSG2+G7qsj|W_V)I-$^1qp#cfM zc8=m?7Be!jXl3S%!&ilV{zm~>6YY#SOjIy1YZ{Pe7RZs)4mcl21Pb-BHga;x(y}zv z?T>$!PEX_V^lUI)t`jh{-hwzzW@)I|QxOEik>O0PjYk^O^h^*ZoD54tY2X%V>OqM? zjxj_7_csiM3}`la1M8f-cWf}5I) zn#=s+yh1gB*DbQ27T{8qkoThY!?OlQ=VC2pj>UZ-B9q|s1L@An9T zi+MY52t4kUdyPPjA576*;=wzk777&yoxOXqOrt1`T@t^bbpp_9Qyj{1KouBEDRUt+ z3wN_C(!-?{BF_@N`I&%AUrH%~E_0H#lqE;0bSms=<-_a*SEem7+B|Yf6H|rn2H$%M zOS{aKCE+6(u4{GXfDft3DAMB%Bmrdx*mwgXT*>TVPw(~>Hr zm=T|tBEKEL4FrT+$O2sghDQS&>IG4`$SxE~ox{`Q$N|(f&9mPpuw=P~>I6{NacZ#^ zA&ni_77}47W#tx}2AvdY8ibiwOdr67l|-ot_ZMzI=vAz1=QFt2z_OW54scjvMtwG$ zfa~85FaBmTXC=WrBmGKdyuo5@EPc_$c;uA}Umy#c@upnBQ&jl)8uPkN0L#b4ijM~C zO`mJ4jQT-A<}Swzm71&6FcnAe`luD(UibJgR&2Pi4&EEfq#;S|i2 z;sxjx4qL%}mIw&{hRvWG-`vvmjDsk?^3mqim4=w{>HKM}q194_Eo^8x!K!*NX>sMr zlUO1TFE*lt8)uMfT4hWt@NI6X-8chBrvwHn_+vgp_xXb%eW5QOF#DZESVD<+a7#mdSw*8vG^Ba5 zHLkUtWdmenC5Id$#cm_~Qy~xI`!Fdw*UzcLQ_G~OItk)xlUK?SNOpX^%1aj&hPQFz z@_6Wg{lslHvdSnMLRo=8;`Au?Jw>b=3i2uI{MlhVm)i*hT3F+CStE33Q@S%pQrp)@ zm<2!=HPMU|%cZ6&( z@X*TaC(|Y$BjB|YxrGR=_dQsga-3Zm-SgA;{-j%ooP)IcuR7@yk#2f%0!TH^ zXOt=THdeM)$`MQi3F9$*R-bdVISQC;1NcKjGE%E#Yn|A}YvUIy8SoXUq?JK~O6t0M zuVkjNLCwm9i2Ez+PxG{zA=*!Iw@+!qq8W4pED6>_+%W*O@GsTV?`D`wDVf3 zT-gzREkXx=%?}S@twMaW`}AF@Kf?s<0AEuyB}2%k2+J>CO$v(q=qHE?-Je(r*uY)&&e7S&PwQM&md}D|P&GwD`btrjVju?hOKVh+66pTi$`{AITl;AgX+#CRT+R43FU>( z%0^BbfzTERU^Z#gW%48O+SM2BV7jw#%<<>igAQ5C>CXnY5+zjJ`kF?VLpU79i}woQ zd!DZKP~aum#rFGt!=YZ<{()xWEJ`noAA;(TmK4af6$031u*>a%Ztu(6 zOf8Kw^!bfFy~Q1<_3_~c#|!~h3J!E*z!l zShfjYiYKr!aLVzcL{UVzg`g7jv(cKX;@o3`N>`VJ@YBXxUa}=2Ds>kdNz=!O_XzYe zOiPktM<`BL#TW*C?Gs_>XCUCO#SGERyePY^YpG-_?_f1N?Ooh;5WNM^iBTq7>wACy zS~Ysqljzl7JXh*?oLr|YnjJ@VI7ea}PH~q&5+33$IEkm+7p_0k8}4@J)?m1>2*`qm z4pKN5&tc=IC%85tvK2HJT7$wc*!N(?ubK3|PRU8#ptym9_Urb{*W4E*Es!^1IZ+3i z#c3gPvDVvzT&Tb-SeyVp-A(PB0tM0z5mhtY{huaj3x;{}3a{U}XnuiI%Pl`1Y!%9A zuJge*9hpL^&m+nx(P_m%TnI3gr{24A!1+osDI$=<8*m}+q|x(G)$KWoR_hzZK;uHE z{l*p2tD!8#o<80?5hJN7F%WOw>v{W|A$PV|96v1T9FpUdfo%t1V70MGG|O1O%}Zx3 zd_F(Ha}m`w2yUMpjk6{7JcVCJuhS~ZnNvl+p~G$re}Iqo7%c#FQ%i513tSNe9?d%} zv}P3Rd@(M{Gb~%3_Fiwl%37uNt@3nz2v83}<2mzvwsaQIJa zE!-AE;#NwS1&-%az%k6(LgYVCk=DN@^ElSB60}vvLIgn7F$*~3#(7%CPDmO&wgz7= zI&WQ+B68{48V6l>;qrEv6q9RatpT0sf76$QMehC!6PsX6tA> zHJ@(Co4TDdl)^2%^i#L*Rw#}<+m6ex+(#a^Yy`nJ*@irNw%Y;Q-HnDlBJ^uyxOrQmGHwo9U!dQ9IXxgarb?Y ziyxz4YK_a{y`B$OGwoW9tZm(^^=qst#oBQ#$R*mzz&H*X@Npz6foSKpf?ULCB zda=|l0I<$)w$V{_SnC?a-+`K{s;0F4sy>u@z)vJXJuE6#iKM*7m31bRIPb;o9q+5=WRjyV#b83s`?J3+0Ypol*5JiaqBfV zYco?O`RXM*v9O2|Dy8mQepy3QSuwPu8bmq=!POg>_Ye&`;Mci08t=(w4bhBk*a!zO z0sPT#E*6&}40cG{WJd045ONBj2B&Gs3`g8Q4bxbpymywQ;oXz9PN57kMRS#;%AW~l zYzD^~l?xlz@7q&)_I=H_$8$Y2+N8SC$F(b}O9SZQxWuUf3hj9Egy1bs4{1>_F1;1XoOGw&w7B%Alx6V+S?arLUFtr_2pswY zq3qoFe*Qj;5xnn_J-G5!_wy9F#Zf;mE_*ep<>YtvqA-i*4*Nix zM6STLT8IsQgejAng;<6KLc?bxtw-= z2i8djmhEs1en#z|zsepQ%;Xs}N?R zTpq@?E$ zcW(oK_yUE%v2bq_c>sfnDvGpYLi$xn_S3nu>^BGA;f#JbeA97&F$F!FTTt-euAUFJ z6Z*>05n7!}w$H%Eps%S=Iu0uy9)IMBfq%9AUU{9MwRCj`^LkmvhxU>aVST7x%c~GL z7c4uaf;F+nn9kn&W_z&Sa&+z~U!jfdY&(@{HyEbQJ%%3&ef4hP=AFH$8x1lSOrL)K zK+jgI!F`gL@kr7K6#6WR)FwrJ45c5_mhQNt=Sz;eO1|bB&IjbQU*1Ey$Gra(V#WhL z?g*aMa4U0Uw8{EY+Iuq+;B+p~SFe)It-7J;V4-9iT{7e}61b$Lw40`O{Q2$raiW61 zueA+h+mMui-K;Pqk|ACsD-;vM@Xf->4V4kC-RGNppIFgZK)jq&WvE+!^p8?Y_4&(J zLAbQ^OER6jlxoFq`-~6w$|T=UdZ}|HT6uJahzXpN3~ZBaP!iQE{uB_ngSE<4m<|!z zcC6Bi7GfC{G>-uY?|x74@ZxrXGoChDr`4~DskxL~rwvtZ93k0PZ%#5VyJYYPwHW-; z$QVObYUU>|2#6{(=<-4rQ)g!*FTuROIJ-yMH>)zMMwD;nzMHOS?9Tkew>)aSKVkB$ z@Rah#N&dFCC+mBso?Eif$=J|i&K#O$_-AlPI6kl1EG&O>BK~FtqYp;+#IF2MPUkvh zeiD~W6pVwKq?u1sq~Bqb-dIVXC>WJ#4w~y?<=A7@$LiGPObSj2c6VfKGa7XQGh3JL z+?_97w^=7*%>g`*bW4E1W3pv~;pdE#>dW2pPkastht;M4fqv|MjUs8 z%J#qj4TEe&c!lZ$bwNB@VW7)l`fPT-YQCt4@JWQhxZvdvZc)t~j za}WGZ{q}P+vp&`xS5W_c{{++APGl2#vH>cXf%-)fa|f<4R)>0Q^^Tm;6@tU};-FLhT)gT*x}mm+@uEB!Aqf(T1W}t;229i!y*kc~{yPI^=bSTscFHC|R2B0HN9x zVKpm|NqHi|+beiqD}!vc;pV#0R*5OG zyn;jDYFfrF9n+-|Re?`HJfO*jzEOE)8KoLvQRu*y!GTG>`QFWVK~@2GRJ>s=&1Pc& z4mBI80j6#9QTA+V(w4Lwef{>aoX2KKf=e#ZqRf-lT-q(#F%r!6pe*rS=6t=F@tX&& zs&3PL4}w#fAfaO;ub-Dcg?i>Q9kh9?KbnDiC z_^k!5j7f>CKFXFEQ3FIN#yl$OpVV#?hZUowCbBFY>F#REAb6Tf33m|VKgHCT14dO! zn&D=@e_B#1D%Gwh^a#sb4c+svkBa*FqASAU2_oT)LTPP`WYC&-KG!qxtfs-yRF@#uk%%OtM%QYc`+ib|vMsRLkFFgG!I zkp=%<_!(^}80RD;TEHCxX^W%poL35WD@ODQ$*AB}?0a+)ZGtXi_e^TEcLcpCugC0J zVyM7q2>MxOu8c--&tkrE7ojEIk<`dfJkci5QmQ_mbqP3sv{0Syn+Ht&FhD(2q zq-smh%-mWAuVh-md_Y^-{{agS|F|(Qk@npW`M3xh{k>)w;O(FbESZQ~gX^$8e#wJw zsL%T$p61@xworE=ALuNTr|z<@QeaY^-xt+OA4y5Z&F*zOl7y&lEy3$f!v};O$1v&@ zPX!;30SX&EqKl(aOH9W_SVrW?c~}k$MhO`1al%L}7{(PHJB986=`_j!1L?Q;UKc2o z#MDo8K%aCWVb|1KHus2UiKa(*v$Vu$NSKd2T(2t6H$_#&N<>=DNQ?9L^qiZb0Eyz6 zi;O!|r56_)nFp=GOPRSD&ip66pINI>EQv?9Sq?2T>hn$v*miIzG6PCBAvjME_=J~$ zcm6-HvmJnUoM*3u8Mg>wqW%1g@Xx6zBRm9j$4K2@hKR-LluaJKG_<1F1DM3C}CPib6Y43_|BiDN6)8on%}De z!KZs&q0oXE;)o?+%`K=!9MQY!w~Gc7p}(@mvm7x2+j1^1`?`8R-qqN%XS|Z~Ew;*9 zXfs~MOu9L=ZiVDWbyp2z?1IZEc*dqM9sniPp^yZcpQ8swewq4);+ zNhxlaJ*V3trD91bV|Zqw$_#Sh_h(K~3p|ezUblt@mgmVIY4w%VoQnVmutf6+`RUCE zSK$|rlX!bkJQTP5R3~qo_r*Fy^tWYp_8b>noe+hNl9vpZr-cvo#e~H)6=9VW;gU^; zn*SCR0{wzYt^a)#`UCYw2*7{KBLMCE|M&PM zF9Y=6&e8lYQJ@CN-~9D|uwQ`A{{qY%jZCaf|A)g#7<~=(3vl$Scme%CRViOKTY><2 z$Q*&xXZTwn7+9e{!7Jb^I1HfTE8KsPER>#o@k= z?^_ujXscQl^x@cF5apqN5rOT{I6$T`C@P5mm4xDG;Lqp+TPFQ4+;J+B4*Fygd1`r7!_{;?JFOq60J%9mZ zSk81{V8ky-{v03@DR6TF>X#N@CrCiObt3g!JK{-{KR?ZjN?b2wq{;%j6-j}s^4R}s z_(iR!7o8mC z2UbsWQ~qJR|I3UC2FCZduwR$Y2Usga1pn+L>SS z`|N+8w6zso57-_C>;@7x)kEK>t~$KV|@;p#0WI&xl!heze>D=)buh4(D5JVzn z{r_(^FTSt;#R0t*`U^G!b9+dEAouuRW2<=i*VvNj1f662YcM9P{5ogX1Eg3H{)O=$ t{BvxI_5q_;-u^m2`5T8h0JIt=!Bmui0$B_g7z^kJ2Nc3?KJe?W{{u3z)ja?J delta 47854 zcmY(KQ*DWo<_WjQ}7Y{XRz3dwMVU4-Q zn%}IHTCm#|FbG8%FmSZ^JoI=RR8%w&kni6?KtO~*5c$i!g#T3<@q%*b#-shs8K>Ad zr`Y#Sw@%+c{`&~&xBr}5L;ue^5&RoI=KuWw38x$_I|K*_BMb-#ePSFbMxy*Te87D{ z9O*B{gu1cl_hD#GL1RfGHwO0Nq?DkNU&Lg);5Z^>(8W|w>&T-Ht)1=jBnuC$yK|!S;vP}>MV^c_r5GVcLs=nH^?rC-*=KA7NJXV?cuDb zS0XP*_AqA;t}qDGK_~w2gbd*IVL+s@XEB%-ar>e_Id-a96hu9C?RXo?piLW%$X3{o z6thF_ILtq7nlQ%HZieLF;a5y`uYWPL6cf4i#ThY!$5@7#D=@tn--Z8>zpi^Ws~JMrK>!OEzJ*l%TcY-YNr$DuvZ&5SeAQcQ`&2vpVPD^i~w>A3>y#3 zrPkuZ>hzg9iEw`#R+-_3Qs!3{ajlg#qK%nNbTX8(T*x!hvzA@>&khT<#UkpOqnSiz z;kisws8VZ&2jH@6o|>$cp12n3Ug>#?E03JkRqwNcke`G4t`38PU9zR|7?hS{!i;3} zY$eEqVL$O&H70pQ46^-)U;*x#H7N~=l&#8xMtrDfSdx&zk=RUwu;t7hXv^|m9!t5~+VRoN)j*{uEY6gE+W6zvCY z4#_@qY<5c#W_iWMD6M)YpLH%QD~Zvgm&QqmFBty5yi})XlHatyTj=KBRr@Snbo(%05sx7WMs$AydwDW?C

      ohmj(wKVBMg2V`Or6Dh zY(8IG;tK2>&RQkxrlW`(B>Zh10(q(zjGgesZVRlxxvdFC?}=}zKetWP^cws&I_m^K z=kOy}3<}o9hR>&$=@#fytQsz@Dq>d%_efe4ipnjrD^+%5~%9N~XNVeysgFbS)>q+Jp7 zq?-oE*R$EC^&lFkzu_w{bLa>6LgbR}ON}`OviJ$pi423;V_PY`;p03WLFt2#5oBNVb$jVhWTr63EG?f;!`v`Nk_# z7CaN^5d%~qEVVhf&LsYUNO%b3D==(`8eslyCd>Ji$Q(235&xhfzYNEyMD{kmX!(`d z05f|(>HA{SF7P)zeC%u})IiRmBqb;5)4el{oY zTu+O8{@nIE)SpfC%fXuV^sIOhTuKR)PrmMX;jboA-}XrnDv>8g4632PZ^_JlXlXmx zY~FCIJEW37oMJpuMXmB1*r9aCf`0<8iTjz&u{LfQJ&n>_aZVn9|H$tDQD}97(xJkC z3cd92A^nfQ0#MM2#gHhV+O&!7kc5Eeq&IKctN!%}YA+5nm`ND)?b^03#dB~<(Csq2 zxK2Eyj3Z{y*tF^TTYp($RV}*(7P9#=bxqc2^Ie-N?Lu*qmR#Cj|DyX}r@K5`L_rw&n9RZgFF$PYewNvt?SlQ!Em8k#kgPbFb|Zp< zfCz$vfUy4eHOT;b6d^P|u%sa~D@V1uWobJby^4m#TFW04;3mexvWeEH3#HVuhEr~# zaDhn%ru&KAtKz7@FM)9ns4^63?XA#u_di_E)9ua{z~8qguwGx@mU!{LkidZ8WVD~_ znnT(!7u0k(&neOHn=Qzp7DfyG_#ualL@*D|A#EC)W|F|7smc$!#X!lReFJ(mppAr3sK4=Dpf}X-92Bq4TEbjrceQCCPt?$$)nGkaX~R@%WR~u9A~;0$ zp3$#eg6<4P{m`Vhbp`bD`s-FvyfWwCmDwok1jXTO^y~lT0K0x>Zk|EJ`Or|hQrp_^$uAzMAf12jMdgcQ9*tQt~1SA$81cdm%d*(a9U}9_J?3}Fy zZHTtw`Pa{?nMFG~NYjxXOA~HYxU>(;))9-0!JjM$LkN8Mi72`#c&Ub$Co(RZlOl;- z0@sU7URZ@kev{iS3$4^BSk8yc$}ywm&sLM1SCO69@3XC@X#68@d&Dbg+$HEdj_a45 z_v`CV1)rl1#!hg0Ac5~=qZ z;^zG&5acfsP?S+ilOdo!l?(0w^>?v%17x#x2h|q^82;kqNIHh+SpyvwohWi3wFN`1V z=-ts@(7V7bKq0<3>AomsUzW}R*I6F)T4Pgc(;^`!@5IS`g8Cfmsab~b<4l=a3qlW$@ zoyBrmcrCM0YZaisq>AZKU1b?^mhnPX%YNMMd~+k2?6Ji5M{!@0xJrhs6?^eelDcaz zeuyorX(F*Z3{f5Tf#b+k*xFc8A{w~&*5K>d7Pb9ixc8jUhD=*P#&-9>`}>nP38D+$ z=`|NwoV<{$1WC@rWt;nCOmUWznS_v<_gA;C_d7wDOAfHAgRtm4PAC%<#mQ?XrqPdY zh3|NkpSHlgw|;OCv$0-`*$NF8<9*2#O5&1TKeJZN#KS+2bGvEj6EyLSfzWx=loc6s zF{UFNc}0q%qd;mVymBB@2EUt<2|1ShGJjj^kawns4=2`(2jc4SK8#Hxp`8Uc2HFQd zoLIU$j~2jj_fj?~#eXjI$OVV9ChSq z&e}R8vTW1lZ%b7M255}OtjTvnT%!$$0w2*Zu#5b{33zUD|iH@Vvja52x9z~b(;%wEAwCQ3TJq^7sO}|VQjH`57 zjRNK(f0#`piFM%Hbu7O4(0Hv7c|L!t)>)^_aZIw)(W9~zMGMcqT)8oGb8gh*O266F zu#X4&_kE;wYb;+yAyS5smR=P#n%!@|zNZcIEf zux^%Vm-DbLh+owhbfy7_CB`i$4=2%%HdeW4HZ{?hD{&Sv_nYBCGQk02BWI)NbQO%A z!h%!y&if)TMU~8fbfOGdTH`aXsAF*b>J$mVi5*))kUm5SlI?*p(j3E{45K=WTmS~e zO_?=8qF4*fqlcwFWljznc+Fiv2+=i_Ld=K?mW<=HMOz*9@*h|(! zeHpj0pW;2_SMx?=1Zy^Xd~KF{s2=MHz74q^$ugO4*$dN(Movt$u*5|UQuUm98S>^y zL!JDld)Pkn{gEEwA&UDPHWMsYbii4{PZ_VnI`S^3AT3L&Tv0eKYpEW*#=|z0OGi&! zJ|s24y^>)*i_y4mGV-HRLVEnIh$mq3NaI>WyhfA*5V$%?x|pWFuI2?MV*R%2<3 zNirku&yOxomx#}l*y?*=60uJqY_dU-jwD%}Cv8SN@>Yq{M2z7#aA-kYqMV^)Mn7S$ zSNi5PZ~eq-6E zz96IXftk@}u4(d-)Rjr-4+6^7ymdy|m*;C7!`G~(q^IY)Sbm8lyjH>XUh)z|INt zD!hN;&wv|2)#psdAyiNd$^UUe0gbg6IP%+dlwbOEoXk^(eM}AKcAh%tatbv2Zi|QB zQY#zPN0gq{q; zXon;-A_cOBbd}pQT`iD>ILm&Vz#Pc~eyyw_0I%|vKo1_}@y*ky#2r0&dQORhDZYG2 z9j#x;l=;uv32cC&<5*-BCo`oMbAk?fV$iJYRHu}0_Ecp91DyFo`lHu(GJN}37TZ!| z*-3N=4in^T{zC2gW<7axS&ht&XY+Hxuhq5uvdqP$Q|OYk)LGzZRAoBJ5BB0@IXC}p zJ~&OgGXM9jumoMMtGSXMW|6w@v-T!KQ9LVG5=(a(xXkd>q1cZ!q-0H{)6rh&9#>gASpE_aKEk5PJ^J=07Vp_)SM$obQ#xK1oRGc{tO# zeZg~lpBw>nr(;F{oO>~A+jQvCniWAXS~OLCNWY04c1N7D`m~-*K*oOzJ9Zjo2`cj8 zts+*m2LAP0V>CeefRyC%zqh62I+uX(`A}zPl~%b&JLK}69LT7PvcsZS6X@3$2|=0r^#XHY zhv9bAeyrAzx4QD$^I1FKV(hsEDaIzR=9#T!aWOGYRCLl+Hf5OoKC#wPRggM5pxaT< zDo3!P_7@Yn=cchQIEDlTV-Bvks7sIrAJ6vx+Qv!`ujoOll?N#>UChQ%Bs zF z$Flfd_E3&g`H%++e@fS7D3rED3}2MVnHXh$K?Z)0D!2|8?{E%$U5c{v`Dd)quF(0o>FD>r{{jaKv9g=S^@dq~B!AjTSTJtsS9-X+%c7?21HLTpye@?YMlwzy*Zt2>S4!LCjP++E%U|0Iw zUYOYpxEr(6J@}`C`VYp0B&)F|oV|3Hro;HAVfe;Wt_#$BZIg<>g)2%O!Dr^#dW6pz z6W-Y1b}KpkYY)h}{Z(El{*h=o);BQQk3-?$`cgk*_bJz4<5oytsLxf!Voq*ZF4a^LHEzH8C>B?$p7e)z z*>cvWLYFd^_aD3Co&?3+cDqD*^wJQ)6A*v2)(nCKhBVo@ye-W7ND1TGTq!-QRrM{C2ta<)TNj3lh4pg z;;4KIlV;|JT8brW+x*;(@eg5Thsl)tXipT#e09bIT4aqbmVBn)9z$3lu<9TM6cVTF zULchD0~O4*e7Iq&d2n$`9ozwh`^p=!PWUrpY7w$YhfSf$%)ZlXPq?-8k+|#^G43>RAY47Y>N$l6uCro)16_PqMJFl-ffv z8u68rfT~tRBdF1?+niX)9f!{jo!Fo(pmGcBD7tUJ>LDTT1Rbw^yc)Ur@wBR%I5&yJ z{(ejFOYkNwp!R@KMKTe3Wuyn$_zl93Mcq2jPOw@I@|8KnlNQ~ERQrjkohq^hE*mB@}Nj=z| z)%s*+r-gxc=nZc*US_)))?h`R6C+PCkOXsnv0&-XC@=)gH$gvh5PXU58ccdL9o^S@ z8*z?+Pu#Glg*tbGN+y)W{@?uL9r~$KaFI0EC4<7zJ!5 z2WYOPrTP`dm}Af-y@Yhn&_bGFP#t z;xDkW#s*{wc=d$1a*KipO^oi*Yyx9h5c&IP_N=Nm{G$Z5wmvvxQkyO4h9_yYTZxEw zVYOS4o{&yeeUZ!we8an7avC9e#&dF$dP=x<2Fn-<#(zxOfHYGq0^#DwF9iSDeG_j+ zu@hIZmj53Mp4zKupc4`VBs{SXn+H(eaK_cZ{=(OqU%%sa+Z?RU*x;7Z84Is&f>1U^ z-EVV@GvTp1O#2NjwK5;xb!)9rrS3RcttbiI3R)b@p42is&;KLa%Azb`*Z&5bg?Hcv z^JW!OKh6K!v%4GlNNZ5J1i$yRd-wbEd-qn)$F(~Ea`Sy#ewR4QsxOeUG6i6?{IPHGe=u&ETdAU$6RDCOwoc@XJM$g(gnq@LseCq-(?+;Gpsb;iEsin z2rB(a1N~CQevmNFu@f~>t=XH3^1|Gm(5rFVjJ0U+9zRg+0co&u0}nrA{Zb-0avGk0 za1!cVC^2)QfQWdKgLaD7WeMmrv$UK|n%;F=aIt)X+KJSt+{%sQvR+l7S>9GBxK#et z6caoIXHVAKnC9w!U`THv8HS=8xkH2=RBqSxQYPX)#Gqc~x8`!{)XqrR4&t(tF`J=J zdn!89I@hHX|I~`qW-hGZNKC(-ETCt(&(UI#VRU|Q>Z?(&l5tvXI06jwiD;9V7MLuG zBqR>Z1(C31gB>-0ir+qlmR8uWr3YM~Rpe0O3$U23w=}Hg>LWjfpG7w8(wf~lt=(Z@ z?SI#6*y_*feTtjQ-|nwE-?e*r{V-c#+ewVfqgwYN6R7A{DzU=RSSohiP68roYfF7J>Yq%>Fu)Nxs_aMh@4-z zF;U9`YFcKJ|5e!C&X7Z`@+_A5xiyag3<)EOzSC|q~rx(0;`*{slG#arQk z$zk=iN|H;KC$MGL0EqvM27BgHn#FmgSE6+kL4yTI{hDyVwn54*?l-1C~S~axb;VH zgOXwl_A5dR&OA^(;qA{}*lHRyTMG@4-L6M6XfB(Z0ZX)1M~xS@Ki7*U(G9koV(OlJ zOc(9<`O^Dt%%b;Wd6GscJj!Ryq~fF#ZCd+BDXLzDRsxSrs*Ulewjs4W+deGJmHB$=^mS&M4zvoX#Pmc>kR*opC&(d@%u|s=`NOmH zV~=ILgWt4`VAm>wSMD4v?}S@)rUen{Z0+y23}72Eh!A10K)0j)k2vg;A?nke4CD`C zCf*2zAOj?SY~;(N{O`}XgQcTlEb%*}A?$*@En%mN-8jaPVG4So!*7wyfCI`Byju>N9e+d1`zcwfWx2_$sbz(^F_9?iM0XIXWb`sB4@eYxDOyqBUty zY(Tu7RLci;V8n89*9#2-gYku`y~T>_W@a?G3EO6BH01#s8Q5@f5BULy1He2SOp{Tw z+1uit^0!R&p@y|9n>{%k$Ia!>q2CS-$J5RrgTF0_Q~)wnk%qrKx5tJc^R0f1t4YqX zj+QS(p7hjhp)!*bdla5&Y=mXtC$j;qlIBa5(6g1UxL20`Txi`vujYsd&b_rQJx zr6o^Hgn=_ANECjAElQj%>f2XeJipAq3rdIhMjRrX3m)aU?>z{ZO7t!G*K17W^DJo} z{;Ut!mQG#A!yl*nL9n+oB5bcw6BR_>{A~}{S7#sb0D=$Hb+4bswY@xHG2IGINl|vJ z8)#)0!gQ8vRMT;OF&q|au?n?{J26r|ISR?p@bOIW3d{m%N(6k5$p637oBcEf?)8rj zg#4oe|C`lJh8-mNJZJ;Q`;m9h5-h@6ggs(cy+; z@IoW})EH6nNFM7EKT9gkt5B)@c@1S)8Ve7cr>oq7ug_c5UN{W2hyC!VQW%OK)WkJJ zK|xVPngWq|P&4GR)M4KhrA_sQVdG25CneL&uyongG29n;f|n-u0jG7UOY>-1BRiV` z!`XSL>_e zz3DvC{0l1L%^Pp>oxYv?EsxTU%cs$;X`~C^dFHr3REPB{>rBgd%QED{w{kpAvO`~s z(c66N_>FR=O0}&NsD+%^qIq84B+YT#8qf}|a!<{CgX_8#^%b31(xA&kEup>092$Uc zL>ZOz7$cg&yb4hwzgX@h!PE@Vq)kuSRhf(ZR#1CUY#k(dg(})li(bn#lBb`Fe!~IY z)2>3ZZ7e^;X9)+!0St{~n5DLjAhj20+#;|BRF$6h(;VLd{zi8(JfaAHBWt`gxkS7|sE_X69?@Kvy# zt|o7RZDHKl)1MFX_|_7#wm;OE9+W6}m+T&fk-AzCP!qy;q#fh9xy{7eNj)K-*nSOC zg#?lWVr&6d2M|@xQM_?np>QkPF4V+EEbv|PVkJhCvH(7)E7)HG zbnl1vYrkxN7{h>i7||L**b1MoLc%q`ucl!o#EJ0R`d6{chtE~juv%p@%$Lahw#cZ7 zYf9eYgg%qa{IgZ!eciPY^>-(m%m#1Q0MNYt<9oev;X8sl!}~z>tJyd*qPk6HNV`*> zp*D453+9iK5=~#RM_>Sl*FUSDahuNFUg=i7b1Jgc`xXJ>T9rx1cxt%gaIZscGxC|L zO+hRg%&#`W&Lj8m)vw)0kbzLuQDB=S(9tt*)X)tER=Qq7ie5`Fca(4JUf=LNUy67?YD^!BFn1E8y03>2(LNN{ zolg3ARH2tG*{b~zX?;Aq|2P6mO5H;mTbV>+ zh+tk=XQNGC7ti8S`jJYu_*!FahYb;|UKSKR3C#vdxL8#+DET9NAZQMPbK1OlC<#*` z2bbGw7~*MYVTkHH+{K&_A3^*(>#Rw%I0|T`h`A3LC(24_As~KZs!K@7vEO&~#2m}d^}nTH1e{uCGQmGZiHrc!M}0)_|w-iEgi%fo9^Lp6!0 z!rOV!hmd~FX*rujS`&F+|W?LW-d7%Vlz2!7MtSeA)hN z&xG%-Eqlr>@`XjKI2aM-uL%L;u4s~)TV=pV^);)ZsgL(b)+5*pKCr^4ZdPaEo7 zVr)HSK!r|`ApFX%HauyY)_`1_(ty(DXo_pS^}-FuM|KYF1H-_`4qj*zm(=93l6g!B zu$v3>w|H;o1<^4EHi;wQ+M9 z>gsy_h;a!z7W-)o6NNAl-V$oKqZhlwU=-B+IiyDyc+B8by&; znQe|FAfruHm!dA7cfOhusnJ`3i0Nk#sRYTe0}%=j;ul5>s$A7j^W`4HR!IECkHn&h zXT@PkD^L3QRo4j3+HiPd(g`KqnlQPH)u;`$Ihu9_}=_|=S4oyMvP zO(PNsCgPL7)2y|+osKzCu%(8r4k`*_^lYO#m1VBV%}bWsV(__4-Fgm-*^M>ZTmJ-1 zZfWp_QJrx~?N(rKGI8mys&3%G08dVmGTK)SMsMe^j#ifuk+C$YDQ2BL4o(g@TW-DVIPCiN>@q3i9z4bUBb0;0D5Bu-}Ud1bist%9Nrv;>q8x=bKyAWtVBc^$hI zuR1X};(x;E&7Sr*+(c~msfLdWRb+5^++sI!23~G19aQm99v`iBby~nz0vf3Ur5GH; zg}<{b_Mr)ED{_^vwjg_dr^nBKtS!tUc%E>w&vQq%e&-P^J@dh=9#z!|sE?GWTIdtT zi_r5jf*V1+qn%FQc=@I|==zrQfsu6`)fJT`yoSxgZ;ySxUn7Yc#as0^E49W2=JCmoaK48zf8MsEIa)AUUkcq?Z0uk{~5@@`ZGTBMA!y1 zI-6;^3^lh)?A|93cgxeuGejQZX0Ab5dsXCxR? zLn+W4%fr!(fk^{u?p;SWxfAR9lC9OtJnf^Q&}RPYdXIhg=gd1e0+4=Of5^$F59Apk zP1IxPeDl~HA$k(owfQ-r+x?k{ny0&t~}PXb|wqf6bVhSCYZG)i%mHZK~`Se?A_MN_|MPf#drRlJ}J zPn0N%BzaO#D&#NEBtlf{_q2MV__b@TYImEXL~5(`taAnriTu5zrrSH$;SM)L{Vr?t z8n?sakB-`A3PU?)r3O=3dbJHrxT5BAZ>_B@;m($a5v>_O0aT6PTnfTI&01+3Fu$4O z!wD-Gk^bzX*o;u$^rzPU~}OOIu7P5s`E(9D|t}c@d>OY9J+;gW%o)AA0*LB#SweP^?#RG&GPd1vq*xW9%DcuL!i8g5-uh z_|4JEN6(JhyCE+6YH@mr_wFr39lxC$D*K;cNB2wUBA;0GtE(MgY+h`hnqLB_b-Dc~ zVAdWF>nf)j4qCl4w$St_AGfulo2jd0jC^5ml~>y?0DaYvk~*t0E6~gtCGpBy`e6EJ zLx1qMnj7ALY7z42&%`z-&>5~&^%7rDjqGN#seF5e(o}>sh(d=eO53>EZ;OV6Di#8X zr`GD%-*g5*98YfCVRJ_G-~*q1I)7!QNLb1P(GPsrJbC%3Z$jzOqt|P7L+@_2NFX32|B3A&GN1tNxEknxbJ*so z?%EtTDa7RFan30kly$T+f;eHNu97mw<*i4Uq`+M0-JECI^w{Imsmqy+DSUW`yZ->^Xy~` zY2CY0+N9OkCf3--9_l!ZcOP*e+$PodV?PAuQETdvo7LTePS^{jb4$!iH5OX<5~j*UU9cnuu z6CDkAH_JSh;&@qTUUPV!ppeIN)z+>$z0{Zz&%JjZ9Pp3(2iEuGB%d5aTZ`Yn4NvKa z%cy|j`!mb5Is@fO%&S}kVIH21tIhg`YAp1DZgUItGIv)$>=ri29Sn?_hF^s&*(=|? z*5v7%e!u7an!h&1ek`_HU=NW|K@IU1%#HIrm7Hk8ThvpTrj3kvPzC{A&$CkrEXIt4 z)Tl8SOs|1|(8>QnQNcI2m9~?{p;l%lkd!R-lc@u|1{%P^_N8Sz#eV2_9Ag-5?xZkr z*x63RE>s-Twv~3Lgy%q7GRGBf;oQxtqnDVu+chdEEhA)u#F;Dflqa^sTX)0 zmQU2szUGdhtTXnLl}E2|JhSC&FzHV0penFiTwqBQ@#+)ke^a|PLpM;mRZD@Bt9?~c zy=C(wHm1qM}$$$I8+hU26}O&vJs{T+NX_(i1LlT2jv~PK)O$)Q7~g6Ec|s z!mN^m9;Mlhjcc>(vq<8ZO+7p*EBB*9Y7cO6-1gXT*i9lssuMbKHdr5wIzsSQ%Z)g? zrL-O^Sbbn?QQKOO{aaIHX>HnY2>M#te4t`cK!C34qL4XOWUp5j4DHQwSqsF(Z@!aXZ=f#n-i-om|q>rzPW zftI=80OJkbLf&4g@^tMFp)5G8lszfpK~k`bwok)m=2|*^)u>HNd^q!_iDIL<+=RAwPMZ5#39LL7Z4dHd| z+oj{(9LE{9i{Y;;67C06*kjVCxX#NzKxYd3B#P@cr$neq@x6DXOR-O0GeBz>e71FB+lrGWBZ=r~ZkcLvD zs#Dt!JySn9J8w!NGMeZ0;!l721y^`>aoT2?y3(`=sKU32;mkUdL(aZ6r)up8i7gs* zyUzG?eW=;3(b=`%GuxI!^&O$v0cw31<%aKHpD_1gbNR&M^Uki14UZ9lRGGT7ETPe; zPSDvtl=l0y7Z{@J8%J`m4x8H8o;z=(wYtDz^+jxF$}1Sj;Mdmbz(VVtjevHZlWw9e z2UvWkCnWt?4t7w2qZy<$GeM3JCwA;4Q35aGBa>50UM_3@eXkc>ob}&l0OxY(D$ZVD zTj8+aK@rP{TCdXsc}`@^*^=61Ol%M#Tf*kY!nox{TSQS*@yTn$zI9oY*@9+QO;n6k z)owssWZ&>oW81N;es&gZqb7f+HBMsWxt7wxLg`>Ka}9p#yv1z+$ik(adNyqE-sVD?g;7r9E>YUNzGiR z2y?B8DJznEHg%7-jDHc2p=4wqO-xX;j=X=2BhELAjyOXiR(~-99PBkB6&OT?G5rlv z@sVBCJo^%qL(1F;n_@-w46>X=3HLO+nUsZbfQ5o+tjQbUyYCnkR?0+^Gw|&e6ya@^XPmI4l==d}*dV{ib@6xb z=?9N{=#Qu$y(EGX~LvWp)YKZPI2i_|ukT68+$6v{a7m{Te}0wXy7qJEC3cAgo)^=+A|d1%`2 z4D6?Uj{u{t8SRH=vBRBaP}bUuSYl%;!p;?d9UzP)Ik`SEqS$Z#h={i#{pk4u{r|n! zd$UJdhJQcon9_oP5dHVKhWbA#TPQ=-BmA!~=L5YMC}E;rCIwSd6m}Fecw$Lba56YB zH6JPcgh1TeKlo(UmCc$}ddq5a+~@s5YN{wdK$rb-H>^{ewJoYUnrk9TdcL0jFr*$L z#l>B}Z*%>5ttf1C_lt>n#hw(FJTgK{y*(jk7Cll@#cymG z2`8LQOH?!3aR^U}>wTh9V^2KwX%0{i??tV~i_&*uCp`U!(Fyd+ZlxMb;!`Ocizjn0 z_f(2%QJgR*0PW_~kXsZ)#^k??w)(?t`ufwfomkb29?7ZZV3^DDi6!b8OQ1&4f-f+V&x|FDo`rPrjMND0rqjESLHh6J++>B~>UX1kndBenv^cbg+oR@oH zK24G=1_zCussl>VCi>dMT}Q=gK2f0u^Y(G(-KiW-KxOC#uTKk-w>*39#148&_sQOU zk$?f!Yxgjw$5;^7+(#av{XT5*YyNO%S5u6JjB~v0NBl6R&4&r2n5lglKmz4Qqx*Mq zLf}N^@E!&GQy9%Dd^nXEIZ+=QA#?674>>ZOiDD-pBG`*xc0f;LsP zR!_SH@Ih9QU*a|iR6=3xmm_CWXGQv%F|pXgixQ(HzN&2neozw1XyOD#Dko>2WM!&G zwyrvl#)cDMJ~mu7H*YL1Q?W=Q?}bBxJJ1(4*UL(=Fgy+s?%FqRtT|;y#H5OeHhRE} zubI&e6)K)q{n+P0SJ$VD^*zJv@zI>FW%WBQ5Ydt#i$sIq1+u$20@44?hG5~J9QLko zI6GN3+-op%O_0JML1Ku3#e0#q&4)oA@-Uq>Wq{3YmPMtBar6&WZKj6AyFfzhtZrbtfb!|;-Ya{gS=S+ zc?=9Yv;{&X-`HR3tdgrV)x+uA&vdLaqODjgmIg&+0|WZs0-D648An{yuB}S2LzC`a z_)C9jp-#er&Y>x7@m|2Zo(o3$h@{k^Hj2dzv37%6_Zj>0e!ne2=4Gg*O!|=BKCooh zN$32vBW>BBMndY zN+yPM*XyiTlI63R^6}8o!)>zHgj8XVTSxl=e=V})<0_4&XM+8Bfr(2$K8?hPw87S` zd|+Vh0#(2XN>Noa)->J^PdjCqS5L2(H$j4owVtFed`cV0|HMYO1%i=Q|7Mv73@ukS z5+gy(fL)<#BBrQ9B>f0^dWdXEDYAd@zT&H)Z1Efw*Vuc3%=`#SP2tFVyf7&kNcZ%G z<|h+zFr+uNfo)(vuWmzD#fZm`94ve+X?XWDw~5rUPlvZ2LT!>m+ zw9sDAahcA;9u`(N>=cg$Gmy&zsBJZG%hoiIyZWAx;bck8wb85V1Y}Ix7o$Qz>(f?w zJ8L+ak^|UMm?w#XM$DA$#2m&eX`BWl+ z4vzYPr#uoa$+{O(p}$&C5rCAycgH)3A65+$6uY8s+OcV-Xfl&;iFFZKo)ze68!1#0pf5shBqzXHB@&WH zR)q~(`66jp{fD1d1tHf9D>Rn%!@ODkdYDoIuE_cONsX4O zdQ=8fvj6nP3x~tq1H4r?3{ex|QsA`v?H2dWz=msg>a|GGm~McjPtP<@o8Ae+cKJPn zpJ0Z3HX#~9&AcZ^SGeq)uZI?|ErhT+O#w@`XM*u)uD)+YC)L59+_6Y+wJyz*&I>*QHCCBW8PYsXv>|MNrUeA!6*gs)Qr z{3&j_=O|mYlrSj_{129*=tsW2g#R?+Jt?=I#O!t9N80o|5&BPc2(3K*ArqizE;l?x z91O;q(@WR`5N^X@&u~;2wY=xR#gzrJtwKIikgem3{_J4YWl$=7uYBXjxzm{n2_Ak> z7}xAlxN8+9<+N;mB|c;CGt<#_?4PHgT<&Wzur>sUAooOrC8$b-VqT)DBxOWdR>Oo- zIse#4i3|G;*oz%7#i#07$YBhBoUxS?7HgCP!nVQyCpN~XGUs>#Uv`3rZvi-+S_CK- zkX~`{MtAzrrg?zMEik5F_0cpv6Skjw^eMT$WzpQ z%UsQyi(l%_!mICJ5HZVPZi!Nb7m-D7Zh)-fi{p#3JdXb{ZJJQT|HFVON~z1WrAQ*5 zotJOBgfaX#Kk}7tMPZ%kqB=J)ymJ1*WiPSI>(MK5o0)|Sh!=BQjoL3tfhkJMssR7W zwv`fGIj>ueJd1AuB)?-V9^yQ;nZ{YkRKiySe;-ZFjf%hbhxO}K9CeOMpHbo;FF?Ym zZw7ih3H)H}dEOYl(a)UMhYdM2^C7$;l>Q@Y6MIO1qQe(;_&}ngXQF)RirJ@sDbp2i z`?id(p-_&`?wl?o)EeKWnQYcc0#Mrs;e;^lcbg?A1#&V45!AA|=|cs6_U7#q=^`SO zntR}l3Jg|8qqeLNJvE#A`H`1Jwz6WSs4At^$xUy?-GE!@cXw{UonzB1ZI}trHk7?X zQ*zWY7hbhFURX6`B+J1v#HZ_;N*pIYZ{>PDQgd@^q_=oiNhToBBUOHH1~l7Df8(lY zD{AEO_bxvq+lt>gvmlei7S{K!?U&7p5pJtwIAvN8w~TlRNfjhkw)>K{;JtSr@c^!%o~^&7MwX5*-K}<2(Wi}R^GRgH0RsLR6GAXX(Me&=dYDS3BsT> z*oi<~NK0gt=?>f;IzlF%Hx2z&9B!OqwQtmyo7qIo<1ZDI>|TS;29yhw7tv3u{n7gu zgnS9HF2;roL`*~w0{lkLpX zy4Z)rDL!^O-#PrO19%xPxK@hMEoGXH?`S@Eq*dpu89jwExiAz6YURCb)F?uJ8Xpe?5``BfgwwYINU4FtcL|8Zb;UsQYO}K zh2ak-RcNa}&L|&LNZ3#^G^2wm=-5zxPr#nm|mR}y_|Kb&}C z+qONiZQI6)lM~w~n%K$2p4hf+XEL!hdGp>|x9a|HRe$N~{?xU5ueE<`Jq4#bdK747ABb2}&L!-}blj=k#$xrXc*}TMjM^MV$kDLQI>BpAB6vy* zIO1!pDN`8|(&CTHJemX;t~{tD%^ZzFgNlB8bg3*Vn#uMZt*(E+O}CBL$<06xAod1R89y3o`! z6pL6Z!K_)$sx)Dhx=^WG&uUi`$*3cXaL(2*Lb*VlQs_!eSPObU-C!0r(ug`_@Fx_? zhQ=8yEAD`iS}crlfC3b_lch2gqaH-t;Y5S&b{D7I5klQC9P;&S=ugKAaJItH6?OJ3kaW!4+BC+UusO=eF>+isn4xE-m6N$?@(uk@|lWy}f`b zELx9XFZM(zuKyx8F;jxf(6$1AtM64XZA+lz{vp_w0ol6UW_Qr`EUTzDf_72DeCu^X zon8G++ghI*%&--v3qz-P7wjF$*EQ?bzVQpgV0kj74sdt)1B>`5vu!QpswDaAnGvY5 z=a_uJ`p*WdYM`jukxeBB6BTq|tUDz;M(&MWkS`SKo}tWX#MLpbif)4ozthnIuwr)P z+cHZBic4$8f0oiJ!rR&AWMmksozvC)0=#AS>-;)5%p}F)U1$SYIw<{|;wf212y{k& z2vLn7$$dtV1ICBdX2Uw^{LrHNegghdpZ`Yce9%A5d#}f*r3@a_7uV&h2pE%1n8V|GElK!`^ zztW`oduB_~?RTQ#q1#pAL4)-Y753(SYN2CwoC?_QK;S@SL9-nVa(Nlp1wpJ*pbT}9 z1d;v;)zOVudE(>TLB1zX_A#7AKY76gHLEL}3$5V<8r~yV<|!wAiI(rN$Rz9x`Pf_- zmBn@kr%yC#;anc%?gCx8V91V@O%GvbBLN9H{@t5#?1&TT$)h5krd|enZHS`w7L^ra-hdb~7%xp4A>=M#*i`M?h*_!Tm+1%1yKdvPu5`kf@eV<7jD zZm6YKTB}bcuV_c4(26YSBljPe{S-%LZiI|apbc)<4`67t+!@9z`Ufsv8;l_%1|sEro7Edv3o}P1?fi}n-*AW|90Z_V#g54{ zDDSjqYY!ia_b!S_EQ@DS=F;Kt!HaDO$xc2<!^MYao z5e)uHjrx7(J;TlXh6EeM9_R?+@e#8Pjt;r&p6o`z!=AKV@`M4|sOFQQa78Z&3xxa_ zyTL@7J%uFtfDSYt+o3>fdzB%X6wJW{ zBI3ODesqKZm60DwiWsCYEcu!o0<-3vzFlPbQa)nWzoH=(r=#W=1vwjrT^kl1oB_no zeuJGk^!mWqyc_)Rfob8-Eu(zoFn(+@o0a@Jf zEmR0va+HqpQ0}Zz9IGQmNNbo0kkU1SAoU*n4~9t``kRf>+>f~K#nWDJI^f)CB~zxX zZl?bPd@1K^X5v;ycmCfhChLvV_22|Z+g}KHZV!BL7-Ll`Vmh}(8KZbBrizp9{Up?~ zwSW3eQ}mk~vsY#V3r#hg9ah&Lgb-cH`$0=N4}bmsv92qTZp|p)d?0IFr6*1WrLaO+ z%c7z4AcP@RCPAWs{Dd?yxq#m@{;+sd$h)N9G9J2!zrB4|X7WTAoE>2DppLBRrA=zm zRE`w5JY#a$f*+EW2;SL-h+3tv{wqTxc3`ieT6{|r=-X!0?;6UDn2SMyOtK~s3**`ngM*B$f+}RN-zLCO#Mj7H^kNAx%qdl@Pc;z` z0I~3}{|$kKjNb=+2n0`Swvi(At^D((#GYr4`b7RO{@8dUQwj0EWDcP=!e9;HU|^b{ zT6r1Z)>7+I>x1S6r7rQl<`$X18k7nY>8fZ9vJeK<7z(w4=YV<9b1epPP&c zTjSR^B6EHA4;j0c7DzG~RTb()fEioWPZUIo(4{J-WV3?Qu{Vho97_4MZJ~{oisdPd zG_>FWj#M9JRE8;Xqi|ilmxRzAEG~N@`I9b_{%nhL{XPgRxYx2$k5;#`P8 zrXQ#W;1CSZI0M#T#6?1Ba9PYxqmEUn;>Aj;H-cze`OXjNn~f3LZl{_I(2UtelLit zeVu|kb$ZtuQT$ggl=m?INt0FcTcEMOioYgbos(4mMU#al(SfO&He&cIn1YbWl9AwM zxPJplQ&|;4p~^k*e{)9ZwXDjbwA#~ppt0rG1Q#+8g z6~y&CaRc~0EfWJHsa{yFww{i=eWwk!b6!q>+g|9sI-7CM+D?7A8L0Xraj+$wi1=*U zbnp!ZDMJf%e}S-M+UYsT(uz11Kb<7=_tWg1#_LM<(NPHosL7vGI{8DCB5r=d3dPJw zTq6oG&tt3msT^;hW)C*936$;2(RC9HYmr6nRhsxIr1UTia1YLMOT230?4-TCuymsj zdy+pBbu;fWhu(y+7^Z-j%byFPiD`NG473;SK$c_mQ=nrd8Q|lDTVsN}0>h$W^NGD+ z7B`k$xc{#@QoLN;#zddhNqi1epV=n)li=Xv=iTJsUU5>nX7Ytni^&vjO(b6tqbdAg z=EHRNjg;mdT<2wlhRZyr)wUFv)OL&Wp+b`~rt|4noUZl!Qic4Yn~&LG4+<-uw}W~# zC-ZYwr_$dmlb{-9CLnX`Z3(@lN}v6E^_nks0M}@zZp&Tz{X8G#@GR)kE^z+Zc_Unf z?u^%LeiD~bN+hpLoo#u%<;=c>&DTgxxV8OA8o*^hsFgoEQX{BY#40~(k1MQLmLeep_{ht8LT!tz)dPwXUM+P(ULiziicmWB*ERuNtB86?9*;_E( z+XuO0`FWhd8JV0P$B%{(S_DoNHUg;E!dtMEBS| zM^G4JI5m(_-kq3L)nw>Ir&i-@?qQ7$ZZm}rR`IiU_-tvrJvvOig#~=_cMb^<5DRD* zjeKwFaryn1?*Pf}Bfnv`1PEbKpC7(6cl1;#ulrMN^^%SzM=AZm?<`QT)?Nyx_HXTGH89!C-bKuhkj%Ns1()iA7 zC&^9J%S-c2qNNb&_E8{s4n_PZGH!BN&*o6 zd`!Dngmb{~ZP*_YSxwEI+>|8Ak5BP&}>D`bf4ipK*P z8~O4N(N-D$R@u3%*n?OBA%UKvJ4}a|hP&g+=9x&OS#}NLJ}kKpgiJC~KUKKF3X^xS zUCq`o#=wB?UeBq-abZ-Mqv|=qfGY%+u*Q!QoJ_dzThwq8X?_Q<6xGPgcb=C%8duC+ zfkcvlWBB$89wD`-L2}{0NjpM`z3lRvI_Y}-jVcFRySK{vHscC~;l7D3ZJcA=qf)@I zlfy&l`$0lbnK~)3#_k0}?H|#4<%nbK0gvvP8w_Ym>2P5XD}Xrvlk|R{O-S-xbJ*(p zoq%uVEd-W}(aEBc4WG*cv<-GPayBXIyUeNE>JJ@=-?{)l!?^yrjWLHMydf$@PZ15D zbx$T1TkeVy_eHcsyh12xI0Fekcm%)sBhCxQ5qZp((R`F!RhA9s!_a$0P#VDP4XD0P3P!-+4e=`#2xxve90nLSW{7LLURoDhsvvUgwWDwLrQP;?0=pT3dxeq_7*nsVi@X>7?b{&fhbGCWayY*|^t7@`HpP0R zTKewTAioy=@BhTEi#H8##y>6@9#R7FAB)=R(VG2? z_1Kk^(t4P-G*GfMkzK1|JXBHxyk}!kKb#%|YL#-PS0lOsf91>AA1S|ElnQy23XkzM z?+8Va!p-shtoqulb_{(loT|H$dp63VXyDHZfOe;FwgSf6<{Ir`=qHPmMKR^hLOE?EnGi6 z#gN+J5b7H>BR{6h+PbS`Le;u1SLH_1pwIN2mZg0R!pN+f6(`6ti`2c?y=%Cf%s^g1O~h!JBE=UQA6`h!;Bm(Lpzzz}sHUn}!%5)k4rHkQx1_4mwjV5+KR= z^^qk>^jyxjFpn9z3g?xFo_rpKPDUyS)O>PaJ*w3-@KCI;W`-O4wV-Ksa7-6ognelb^2uf!i)aDS_+-Iv8RB%v2&@vcBeml<*! z%eKGJ?Sa_#wB=1R%jtWkvU4$Qy09V%3X z3OS$W-2!m7ue2pF5$Kx;rNx2399XLJ+5O?2(aYpnLhq095mAk%Xgll!?(xHmnqVys ze9G#h{a3s>!v(^MA_nfNoMzVBrTVcAMaMqpdHaY5FY`=1_V%C2V3v%-D|r*lN3w7H zV)gBzUaD-*%ERa=R5;rL_t6kr&+z;?XM?qHD-04UR8lLCPuR(Yf7gCu&??H0lM7Iy&+-v zPh8&|`^AKOP%U>&2mm7_@n$$vo$;N11cWJXdn4dY$s=5BI21rZ^Xl|G@OiXjuk1#X zc{EB^1RT@fhzE4IU6QrJyE5;=R@e0LZ4}e@iorBt1P} z3AvVtcvP)-IMu~z>jwlM&$!gr1Zt>*PXe6d7_lkIYE3zZ_KP10M?EoI&@(#+q=wMU z&K0{7&}AeT+1-$zw9r=VmxVHYtHL-vVoC>&`$mNkjh76M#Q&MbU&`UbBz+0;UcN#8 zk8kVsBpxsh^CVf9BO4-8zQI=U&sSrs}@#;ZK~mudNreSFujMgm$bSI?zE zrIM()DCT#t0GflpTM!rR3V!VD{Oq4=JJZcQpYQL``N2{)`u%?J2x8I$(tAWtF`MeG zZ8KAlj%mr-iuoz=B%zwyM2>e`>VLy5?~x}^&o zf7IPFU3d5wgiUeI6_yev$LC$U-1-yWP4mJ*UKo#5xohzkwJhRtM+V`mK5vPCer8^1 zJMmK^P53);>Go}1KsGkpwQ~u_m`@5f2!^{>UvR~h+7Y!d95mYH=IYkeW4}JM+|4<9 z=!1ZbD2}BzFZlkg?x40`B4?#NST^a|>c9H1Gtu4VqM#cjLv zk>dmK{ajkkst89l#|fY5M)^$j!fP0{9(4n%?Me>Rg%dbsf6Ua%Y8f!NRJzy`KGOt$ zk_JMN`H@MW2Keqa_Me2#AQ-{Ev*4Ui^DCAKjVHns${b1$g6UM!HwJQk&MeHS+xdN?xTeW)DAsdN&5>Dr=bKRxV-BN#MuEw zJ4Lrpx2tCWN$o#mVAjZN9KWFt4=}L?N$Fl|hM%_wt8D}=%#QDMH52uu})#4YIf(&#^zuJ-J? z1oKw}=L3-#vI1X#3&*&hboJy?kzn7!(M0V;!OZxfXq0Q_lTajE2n@4-ptqMqS5rWY zxLV*!cR14Xx7TM?>)CjEz;xhqNozNz<}&yNFx`}pgNP3wt*D;Q>2{|gL;&mNKWa>G3;nq zyG@av(%PX5&4VwBL_|k>27WhN0s_N}jidt8cj$h%M2215p-(sO0YpM5{V?7E$z#O( zOA%tvurtI*d-|}%2QLQ#exifaV$T@#*EW`)v7!9e7?nH&m3N)5_ON+o56mnB5~z&= zGpsMMkw7`LmV-O_0Ns9p?%w#fl|ZyPv0s6p+wfd1BsDbzh_tl|Yz4-R zT=yB7k9M>ba7MY;m9^~5?P=fra+&is7YEVaaH6dY>pYeec})jl6B_UKm4MY;e#YFw zf{e`EBDsfyb`jV33LCc`%h_`L1&9{Oi9VcC;`73YLp&4vgJv4bwRQ4fCCv9jU_$c!o$caw4T`4OMgPPbaigtUdpE{J6<>$9tN zqzEWj_v${-m-Jx4cKhZ>Z}#HoP4#cnLW3Z6@02}^%FcpgOtGz6Oq#LbXha@!YX~eU z?$v(RiZvmQ*{MF-Qweed_zg~Y3nB0RC14rJKD~K;0Ua)a1~FTMJ9Q=f&thshQQi1k zU5!%x#!;B9Hov*11h~gavTmw7>ND3Pih% z@P@VTf7HzHAYAj%5$RF^bLXiWyJfY1F|OV7`f2$6F1P4ZB?+~n*X-i#PZ&w#o<+kD zAO4Xo`G?z@i~qRh5sPk>^_FO>#<#51q(QM8Go-bkY%s`_qlY&qk3pHe?U9SBx?Zjc zw1)2Ka&T+Gm)lYrCh1lz{D{u6*#YjFNDsR`Q$5@yPeBf4|5Etn_zO;&hfhLwH_1hn zsv*F^#Gj&QfvYSF^?^cTMv^WIP4g3no9Px5>$JDQaLvCM?^qvb-U-W`<^sl?;)1fp zHmtLiZlXTyc_Ww%x>2`_P*wh_a0@58pdpVxlgiN@NYfn`#}c!SB>Y_6r398zVftjL z4$6xFh-YtU;X`fZo^jQHhLUwbz`jr;wu0?4U@|)YgDIC8Z*+(HVQ7L9 zlK=SandIYQt$Dfqc`fcU_j7gZ&dr}EoD#!STDuY*O)uSHT6v=h>@p`^C?EAM-orB> z!wE9m7eP=VPr0#DjAoFO0KQ8M8{KfD0@H7h-_?4%g#=BH{0>7c6(!?;EqGibcym_A z+1XMR9{DM>r%|O~(|1Lel)9C=&L8CYB=+(8T?{nVID$O>i&HRZD_%64?c!(?h??odg$VGn~4;2)u@b4F(CNuIEDH?!sxE3txoigq?S%) zIhJ4d#p4Nmq5-O>a%5KPQ?nm|H2zzht$%6xxfk4n`f)e_w{~N5m(=(@dHPA_7K=~# zLA=3dRnuIYThGvxK>US4C*3?=&GpSc5&ZzJXMl7McKSVCMjD{Hdj}gc+?4afQ~Sqf zlI>h%S8J9BbG`v4*IYoJRZsRKVeW_Qx~I?M@40{;s~yodrkMc7{>>H}!sBU&sl^E! zL+J+JS-EWgrgL$m>cC-d*nzupy!f!n6O;@oXRq&^ZYWj^z}5gfhhc#9Jk3V>H5x)Y z79`Uylz2>9UO+2O=G~ zk=T?WdX5Egr!q$M)(9r3Q*$b=2tEiy@!l9_QyB&q)IIHYvOfpKQ##pE)J!Hee<4(k zXIWw_mf1S9fn_>X8Da+vnSUEoA9Wf5E#pERQ$ig%0vJDsY~SjJg!Ii=Th^`YE*b&L zYp-tBe;POI`aHz4w`RE_R*&wAflvwitm1?)?#8TfS)-Yp_e!qpY4bg2lCPDxeO%FJ z9)+7SE;~WKb?9oVVG;FNmsgabf;=%D*BC>>Hf=0FfNvH&p#y1hR;R-(#?`Ay!Oi=K zzZ&%05j8$Z6Kzq+$>Jkhv4bPi=V^N$Ib5xg`l|}tQnRtINE4`PT(^~{^?!UYte={H z88k7hTkV%5hF6ce&kG-Z>?%cJCFeC!vZWPiq5M7&qAA9hJZA%z0~hcouzWp}(jP5J?R(BI?WS_<)75+C@W1Qux)!+13SN z%HCvDal|0C*)MMdfpi1`qC{i5ZRc_m`SxRc0zG#^nCHqpg_z})kyBF{y<@CM_ zc!X0oGPbJBzywupP46)3%f@;ri?|0~31XWM`92RWTG5h(79d?p9l1h&^XI6kqJf3j zl_js(V77vK+>|bNsskb}h`!tgagw5jBqKuyfYn^jL-`P^EPII64^bd(eni%fjDE}3 zuOFnP%GMQqS|$)+4*=Nw|I2-EiC+8^>#aV@aa)1QHCd)SfXD~n2C#_#o`Wf zb_Fygi^Ji_3eYz69*sKvqN{No_=vB<&zHR2PtD!pHw1$IkHYgs%WYYSRQAQRBZr3p zlQ{`Hlbb)Yc0ON@xWTUW^x(u=DNR-SNgVG?WcmsHDE64<{oYuOwm^w}4WXD-k3eIj zy!%}>^B3nHgglvRlP&~&L7d(bzs9i$7&0Q)7b&uCl=>l*>;`DOAAwB+fD-NH zG&)MTMh9%wRs#`L z4b$ExKD2km*ZRvUi-O6{50THwIdV^-;-kQuf(Fe!x9a{0O5ytyg?t6|S zf%;=hn>D3;jV{blq@PC3E5Mrlx64QvhQ!)>WzXdd=M&aNfSEi!ST667P?$1(07>jSC zJiRMybUcRx-0oVs@2RLG7zUl}0D9LqOfm(FlWNA=XsS8rO6BbpL|~aq0`9QkvUmBz zFp-ZM)>ScFPT_ki`j`8wKVrDnZ8IA1Q-7^BnC;9K?x^bZs-JLi*|SAjmOeQyL1poDV&alwxj^%=8U z;Zq&+-`~<7pu}pjy6h^l*6iYHZHbj(gh~@wj(C(=mDgOU6d;2ulo_W3C)6dKtgxh? z|6-6S#gx|_36tXouMYBjXX>s;+sW4Zraj3^X>HO&HIutnAgi5dKfcy88L$wBy)^cWLX_2`|RfA6M8MO7QXjx z3T~mk+a39PSYqTkkeqVg{}CqqUvUvW6GyntWLrVkTF(!bW1FD?x3E|jw|RB4moM3g zcy4a8$FJ{y!W4}R&S9QsbFlyY_XL~&nTr4#nnrDuck%-V#{dz`;DX%q@IVGL$c+dt zgy8>c?}DsE`at|L*USE|a-~ZiAxPi#J5a`RK@;QOAy1lF30O$FCnG*qNm=ig6uhjG=@q;EeCG6w_9M4y^j6)q zJ<@$=p=0OcvTMhG`s#~(AOxP=D8S;o7!HS_`0?xnBY9OrW4p*`S9ncEB#H7N0+DKC zq$Gz$arl@fTFC}r3z`p`X{xH~TtyDW{ERRW*dv3VO{1h#1Zv=}&i7r!hQCIqNGG~^ z%gtCIpsf!xRjw?W&&|gjrdT4$TK}A9*IA;=%nZFqHHwgx6PvP3R!jS3#U3(nudL3l zroY&*d_N_Sqv@*{hhtmNOKw;B1PtHrXU|@Sr&aqsCLOuhF9&m1HzL ziFw!9-b_-^lW8Xq2oEVLN2i-ZFfvUo$a7<=gpW!v;q&!IS@}}hBPy? zG>l3Jmx97`7BfLoeW)+us>JwbJQ~BKJpzf`b4xFzRGT@%E$7W9JL-?O*d2k2I~t7= zQF>ACn&zobp};z1bF$8yvy`dEAM~SD^fzMdmWDD{3HB1gl_6umGBA6cQ5o}Zzuuxh zZ@)})T`vthmq}ynZ_Lh+3;32OsoK@Hl3jz&5fQa(p=p|DFk)HjTF3C%1Ps4erPX$} z`wYYUfI_6hd}Ekg6_$7T{G>u}H?Lg&QU3k#xV#W0Ug>Cig;cn{`%ryHEq*q8o)!{afP6j>E_?xp zI3nb&56us~F_5!Pa)vp#38i?Cz7~I{ett{Ke`fK08q0f%I+jYFae%#Ti>jnqpAA#2 zqQ08Kp(s0Ok_3A1>*Pk>4U+VG#lL5by3l`G{h=0a=tsZ-v$sQkOnfn1hu5w4LX2Rp#Wbn9|-;X<xvF(Qu5qylyE~46@uOEj-$=as;* zH^TOH!2V|V5!`C<#{&YvF1!79Z8?;2=A)(4Kglv6Z;&=(RHT+<-A-0udKVoDinLTy z`j%sw$9N<=zPT|Te0t9_@xyb^sr`gXP4ygCX#u!SIQY@x>R%lJb*Szy{b%>>TOzws zhnAuFC+*DKNj8jvhemUwkhm1mpoFM?1p?~@)iL48Xjg@c;tH-6TrWN@dyhLKsuftvl`WrKB1%Q)&lsym~kAX*}U{?=FTZ7$cqm`7Xj4eMGW*i1`17f zuDsnq)kzYNA)=xdHny#0|4udSXJA(#2h`VcnYnke0z70V6cp zb1-@qp(`p-qDj3?y1+OU>eFGPnJMMVo;134*XC)`2K^ zqZM8?L*p|ioz7+<{q3Z)V%(>Y1T%)sCT{6rP7NVq-`yOU)2_G_$cGm0p`d`D>{#4_ zD4_-|U~<~O1#`F@ZNKPMR3@s!?MlnxEFDlY~dL$9F1ot2^jbt6=(% zL}h@;X~IZ=4_H@?j#nt|3>B zbJLO{5xad3{kRm4^s7!@P@66;@=j}e7&5%^UmnjkgqHbXV)Z}a)%t^I!G$2Kj;EcD z1Fs-(SltLXSVddK-7_aWWbm1u6fIR8T{w_<+{;l*YD*G+nBz?FWYZ0J zlI=U=@ZY1Z1kyOlGZqtv)tP15qiv96CT&?*%L>a)b4k@)&H+pnlzQ5TMU*R*KRJO3 zT7(l-JS{INzDSkruBVNIiS1?x8$A;&Lbz8#8kW*Ets226#U>8FyB)k|o?FDy`jYkb z6o2}*EQ*=U@6_WXuK6hF9@!`JI{8n(ja<t`O0Sv={v7VYRKVw$H8{c(RO z6|5;Fg}f0u+>%JX;k-cToecqmkTJy-EMvtG#kNi{l-)OrNe%7{WhO%`8B!i~r817) z^&9n)r>Eg_-AUTyxSx?8Mf4SF2B!zFn{{^pcOzAn#gex48^z)r;Ws2jm3`xI ze6&Bx+*D1B13RbZy_jf$f!~QZLxqVP?&{>q7Nq-VoJ+}r5e;k^^t5bq*7NB%fRg& zCM3E;mSYo9ED!p3F27R2k|=CRfn68Tjw*903d$hb=sb zUnGwb7xj=rKaF%hI@ZFEU=UuDvg)Rf8Vy+E`um*Cd^6G64J=PqJ3VQH5jSKtQ8^KH z*a}T3UD^E9lL#knm0`eZyP@)|qFU3|N4FC9jI+{EYf5&TR<0-55AlRYE!oDr>o&QQvlu4j{?s@Dn#(58rMI3`*UkLHNr`U zfKE6(tnY{mT~yl$0*jIw1*r!#oZdj8Y!=aKhaFYowg|aQDpWZVg+P92vs(>p z7 zA}Xn8Dzh}o{D+p6k9RI3+XVKavPC(d+%6tUK?Is_#w%|x`6_K0d2Lqu363b80(di3 zA zjJjtqS^|X>;&Q8%dudvSAw>HxT&?6s#WP|7A!662MC+hdvE-0e?v((5duT5=NZh#* z3V>?H5aH4iN}(qiZvgD0(gW>^&@tfEL=*4 z0iTOJH2XK5Va50T>^h)tF@L4%|63ITeh9Ekv_>7|P8gcPVV??QY;>Hmjryk7?dB>k z$JIw7|1zNDia1;I3uj=pr568JWhfbJID>18iU$VNG=czBRsD*8w;+>HC7^=S!VeVxbH7%&??^WI`O3Po1uun)V}#to7HZ#o{`&}Y0+Q?e*!~kh#Olp)Ky{8V6G;oyS>?+I^SO2A`-y3Zb%T& zJsUIT&^YCv|SO6j0dS-fQs8^7@J|DgdM@!^YJ{EeCyo$K7sXAt@8vlsQt7w^9 z9N^ww#9jUeJ(YMiUN8}owA}Mh7A_g&{?|xsBXhA+L4B=;Th;$Y-WD(v<%-WYCW@8L zuv)HX9k@REsc2#H{f^4oBjNMuB-bnfignVcIMM|0+JtkwUkPkDZ;q%b3$2-{bM9-N zOQ&!Px77YAnZPGlj{7==>*FDVF=*k*hb*iBV#CdD9i;S2&AzcloX<{`FXT_$XVX2{bI?$2=HTWiVP7%*;eXM1mm!; zrruO^@d^wv9Lv&b(nS+|;5PkUHwxB%5D5`t0UUBz9VGJIG&!k2^hn0d4N0!?IH<%6 zUg_>SHq^@djdwes8-saQ29CMmR1VEB?K(Qp{ySg0%{^b5BLzjLY)UhT=6=lqCvz$F z>)6l;txFCpO|@3gwiWo*Je=%fm(I(Qk9QO4Xw6-oR;|McI%3AAd1Qdi27Xgf3U8G& zLmwm{tBHE15J97DAY7WG_)BIp6J|&S+jRH@KojoZE2dKO8*do31V}q5@cJ}x7H)jM zNWK39Ck9Z(qOJMa(l!64SRq-sBGlffD7^qqQa=e8U#7Mf@DoA^FP~WDSzz3f z=MkT-1+C3eqnxrGgf8n$qA4KR&sSwFDaaJ2NKIyB{#9el>G3xWoI@+uH_I3ZKUqq# zt{J`h;i%HjWsaAlNyeFv8{fP1u(oHL;ZOm!6x)4k4}frsWNj&tBD`1PPB$yiRaD>j zlSmJQaCm5J;pHyit<><{rwDsM`rQ^mKGs(}OoQ1*%b=XU0?Uw*QnnRu`73_xr=~66 zM`rUfvz+%V65y4c{-uGfZw3dhR%VsaBmoTS-@7n{L%?N;qbat%!+{Ac@`V7yfe9}% z_RR$gBqv6TA-ly;lN~@YVVofs>w8Y@cYRu*8D-7xl3~ybI`~Bs0|0+VsIysVxI~+@ z2(_ARy-vNfyGef7RiCT~MkQ!?;VoslOheBsGY@Lw{of zr1IN3+Lq>w&$>X*t?>J@<9gN5XyXS@?`0R$0TKa*_L>dNdT)LsBY7!eWec12Pca{$ zehj^5RvH?A+!+>})3_Ic(}Z?RWUN@^*T`HIxGQ-5Z0uX2trptK&dCDux8Jf7ihOT= z8BLN?pZn#pPtM}~Z)`%n5o|*JA{Cgp#0TiOQoJ~Xg1?H3)P^jZHpc5LZP=HAhe0b%i|2k|2-)X^C)hISK@&ft{ZwW$;->C8f6i%x3f*XYu+ zwzn!fnqrS#Wme|sVi`ixl9ykm`#K_Ly6Yd_oUvlwY#aW?BFZhpAq~IbrrT*CZJo(C zWAmrKKOwX$hD|daRu~MnB|TkNVjV1A1HMy~5!2C&opMU&m)spr6ZD>38R%?%y8EXY zO11MbMYQD*50@e^g8%W3bt@E(CwQ(PTV( za1*5!DQ{WZIJvmXm$P8Mjd20&!b6z+DN2GWhtCqhma6GnT32%OCqz^@s{bBTiF34h zYOOKdHJ$TotBoBX;92S@U7I{%Z4fvyG2R;1o(#RIVsk?Gx*jH42&S+%jEWIDgabc7 zjiN0aUWkejktInImL-LPs0A&lj8;Qtdzw*M4;mgyJl$)=CauTEG$jUB3rQYN5}#V$=t?n_%wNtX@HQ4&`FE(?1G+-Mo?AUh3+fym=*7u>d&)qUXD`-63vY zZ+U~5Sv~ztp53IoMqHM0#=9K}hC1&(ydSx$WAH~t_WeILS%2!zsQe zJzedKy5JD*!?q8-T>|0g1kZn>i`V0A9d0}{ea|-@Gu5pX8GP~}pF8D^)NQ~fkQgFT zuRn58hyUA`jjr9AYa;Y^s!n~(hO&>o&j&Bz_XjUcP@;S9+`zB^B`KG^yV)c2C+(@; z8w+}tX(^}xPC~>eX?G-qnpriJ@!%i6SqBUNr2i0DH4bME2LuvJHCq8x2GRK=wzZ3R zRt)SB31r3UcnDJ{+d|kBVI-9nc&5!7F`IadcJcL59PEiCruKmUt``^M)y%1$rxov( zB-ThxtA*cZu9^@P0@oF(BY2`-_T|P$oM}SR;Jd-Q{IT;>h4@Fdhc3se?R}Mk8HTW zSe8EJ4?}}H|0nuDJ9@dl$!6I-giPk!kR42U6`%mL0%fbM_Gc-#S zCLtV#D}{=DB1h$H2A^u0iUlQQ@=jXJ4pz{ftxpR1cA5!}_tpq=tE+;}&lU?d9HkJJ z-LkO{3$D0(WwEwNc!>*k^$lk4i!@4>zUS*V2IJl-WPfB(OTv`XUKb1v(G&jo#3yDu z_gPISXc*vVSJG-VtmQjX4BRTK?>lj0Vk`;^V?C>sIE)o&)>}Y}0rN88>Mp}-vzVF! z$_{bd4=4aR4x!FyWUC%x&n^?kBns4jQnWJebK~UeajE-L>$4RSIziS>flq-i8{DeJ zYP<9rmCh#I3mqi++_FePm=a8xUrNt@zR~j+j0LP98hXwwOtw9$KlO$C(v-gsF}mbn zLi8S6N_f9#BF8v<0GEL~hYS%kSh~^=dUZcmz6yg$Sm0?{S4Z^iW@S zlx4*S0+;GbbCA&wj2<|=teVvx!s+J#hm~S@wejnAmMriTzI!Ma68 zoJfE~4)NLG1Jm#Z>OzTwbh;V{arnNR@Y4i5QkQtIB!OnuZ|hon&h-)1fv!NSj+w2G``=%VhvHjlK$qaf)UeH1mA_$|b4ZL&mCSJZZ zX;Bhl!pCWHJVYs>$SQ;#(qbVBJPU=R!b}4=M-zwn+mEm=1ya4|HFshZXT0+c&nGbW z<&aagx`a)!s6}i684At>@u}Ms)=N5ND%S|c&}Sggs!3yUz2?Y&C)pK3&0m6dgI$Re z0zG`nk&eaq2eoDw>@0IdV0od^Bzk(U@axIm*UH+98jzI^gSKmdE(v0QKdWl~r<(JX z^!8+<*uGt?rncV35Oq zN*Ke4Fz`O*-EW<6@t$ZsnK0M@fYBIphVt}t9JG*X?cfQ5vC8EL zmDQcp`WQn!$Lt$hGyu%$DP8eQOZh66u=);oA-XdR0=wO;KWqRja)_w6MTe&5-nsma z>yo5XBgJN4JaCE3gSNUZ(J`UT#;Vk6;cypio6ABy`^yy~5tEMb95h>4iq2p#b(q9{ z48__k&kr+UcjzwdGVNX?jj;9V0{LS_T0*|jv42fJ_yA;lX#!ZsQ?EiG_9!=9MRx9m zP&MO))X~eA*K%BBl8#D(@D?6!vQt z$VGOPzV2K<1aX7Zsn>1s0qNDU+#0ET*O?2}p*yB~<_Ao1S)1;|t!ppL&3A_ZA*j=hyl$Y;k zP$xwKa$+V0(lv;oG%5K6C!{{8=bEbI;+d%|$L!3B%qj_bF9=DoxOaC_Y-}VMmL?M=LvsAG<&nT?w*i?msJRSxMLcjlb-AeoBQ4r+U~|#Az5ln(ZA*B<#3l z3WFh=*!BRMy}8UnViWlbA^_Il>5U{>kHxPTl}@~g5BQn3s`5?iE)!nkF5{pzO&5%G2bBwDfDPnW8QQCNx>fCTB+$gpb42nLX2s2#zH!tG-Wzn3td}0%TFIE{Z z&|(7Br)<)c7CG#Q>hc*5-1t7LekR8{g}7xroIT5&dTPgFMc+Hw1w%*2^BnlxhpY(j zs1` z=hy;2r?pN$AB67RU>%0rRLbI`SLmwSB;r?%dV73-wb{zc>flcuR#t$>uS6fBkn%Ol z6lW?gQj0BLw59HNMY2SpgJ=c%LZdAZ=@)E$l}Bz+@s8|Ko_KLHXl=K60b}dC?ku*F zp$xc0%qv9Af-~H4o)*Uuu;TDLG_0#pWuLy8;YR)vdPY}(tAZ~Oe_;_l$I?hI@Dnl{9PUF8;!lFo|v`D!BfB> zS_9KWCW$}vAtS%UHoEsXsl$YO>3ktOMPElV3scrR-j4A3I385M0LxJjQ&m zDX5)#(si;n3>C;n`Z={=jtN$8kT1fI$(REs#Ox|(T11X0<``n$c$vvCDOXTf!Ow;> z_&LLSMj!u=p=Tpp?v;YH9g0Cal01J5y)qRCp!z`$Uj#$2UZ?($s-I#`y3=+RqaeI| zJE{QSRj+%V?r?1=X6#0 zsT0C6T1kj)@qEj;M{KNzO6;ayeL9oX6t7JS(VqSkAv77}CvD7e4F3%DARks=c8Ze% zXz9uMFI0tTIZN72_f|JR(+ksIO>94{GBNLB~D>W5i%|cE*cr%<1j%A3KFc?%V zqe)iXvYOT!V^)kke5K+t)4l2jR1JM8KsPKhXd1C-HM$^Vy{y2)9fo+FTJu?jn&>e5 zj_yRYj6D;A-w0f8(7TkKfVl%cbb7XE0e~p7G)*VR$h+qM^zuba$gp1_FNc z)*n-hBotA#4Y$bS2Ji&mRTHnDv8rj9qT$YK5nCF#lGG|T-6G4i9W)dtLlUW5H4?X!cA^`KokJTq;PDn)egcYheK1D8exdrD&D>^|19JGz_8|G;Fm) z{b!e}EFB%7cA|)`f+Fa#QfH~K0~y(XU`CsN7Z5p{hJz}vjQG-K%WdY=wsOoQ zN!&+$&P?$pf=cM)S)tSETA*PDn6UdP$itJ}!u<5*;pa2;M_vYxe0(*$2rDGqq+niP z=w~A=GfWpe^`zhy#$I1M5Aovse%x`KU3p{9M+%ZP&Xu@=>*^UcSbNE^;}DnrdJZCwyjiwYKO%kHAk zehOI8MuWavUhc|HUO3CHpdBfZu?nd6AIrtNeCB=C3$0q1l=?qO8iJkI*Cs z7ai{b`x^Qse10LuZIW#Z!Pk}+z~~t3 z(@mAS3cUFm-SsOk)q}9v8ip_1P-Xx&6jxgYFyMw*?m4Xd1;Z`$Soo>2l5iz*yd}1v zf1H^7>s{dDM=VK!@NN&qhOx;F^VYuGt8=dcBOi_Y6{g(2x#RR=K5rt_=xL?2JYwy< zh-n^~kqFwE^u!K*Zx=sPyTs(&`T-u%?liu4(`iCrE2*EK*%)QcfG?!Qxj@QzBN@ba z0DwcWholNz72PatR-81%by>1rp2p5_>Mqoj$t^sV8RrGu?I*02k$Ym!loRX;eP}H6 zmvOyg6@O9ZA1K=L$pTk6%|koTesoC@&2;wDeS>h|Kt zLi6(a1z%(WY~>Y*MV5p!VmH{_6#Y9G6O*%9%WEKUGrM~fd z_x9z~u37ZZkW5|zCJ^>9APHP_5H^5;REMq4G*oX7WbF#Pr2f!Xl+E(-sVeZ4wdi zWbv?Y6OWo~!tfDgnL0;uH398@QAVZaZSynpQ`x)1*gkK)LT&WLc-p#~i`JaVwgL!Q zOx{G zA#o@pitI0^-fo#WN$%$N3DYEJ@bJ0RK$1fYKNoytOx~TnKY|K91c9}Vteyl8#O?~4Ppf8 zcv7hLOR8}`jHqTN2MZt)QS8BE9kKjZtC&_|Uym_PtGzCw+Sgxj%jg5n8CxP(&X!B3 zw~<|M1O>Bhj6V{U!85_g&6DU*$>`!zz+-QoNpIogv(skjiF|S;hu(yiBuS0xpMiTp z40-ydc|j2Q0R8C!tE`Uow!AUlTq!TE3^bs)-^iDhNSB@PY+cZ%1Bx@V-{C+tY`XVOu%UZ* zo|N{BHxbpkP-nasRa2BV#M~%VS@zndU1=X9UCrHzpM7yO4`sMVSwGPa zz+RnGw2v|TIlkH1GEW99PtySLy1la5&evbE^hJrI>of%0dh_eg>uqjxW8E)#WDMWo zG@spO7acemk1=cG5<{?z9MW%E+5ixIKXQD6(j04^zQe6VWwdEBtU=$hU|np=8*C1f zLDqKzwBA0{8XlmYquJN))_sp5X?uj|2XJd5E<-u}oMIfa6Gbi?;c=7~DaCT(!uFp0SZJ+EkpT0)J>Z z7zXUcW{8n_&ptGck#mfh!$6dmSSYBt-RzXHOU34^+7oHzB8lXoV_zE#{Dq(_hY0}* z()5Rg1$n56fJK8@rEGyL!yed`Ck;?g10CV^s~@5=s(%NIa)E%Vl1PaHn@bi>R#c>c zp&R17$wl%0=!bdgvTai5Cg6fZ!&lww9LDoEJ)s@f8 z&oA{}zaCHc!Ms*AQF>Ye-^#mA(ov5VvZ!s7R2$;4tLVjS;|1y6w3JA(4xj)4y=!%+ zRS{r858s;HJeIb1>-5djw4L z0bMRw%`vN=_CGa=NL_T3rWoDP)K92Y>`+Uab~PwCB#0;xTi7a2>R+V0g`U@`6>fi# z7`85I_}mtEUtAQ0%?o8;>9PR8cqGBO$^Jw!{SE!G^`yOl*xswyt1q&{ka|Yct^h7m}D?575o^ zCAiV(yyMmLa^?D1pbNB>ozSCHR*lmh_YVyRx%;@lt~tWZR#4OZTgF0+aTJ=Dqw~zg zzD*8A^|h^Jk1Vum6MFkT9}MfgG{S`sVR3ZKX} zAOO!67Qrd(Y>`PB+rnx9Tpci8VQ(o42a8E&q5;99LLx>vQ)3;uaejTYd^2T`INxx; z8AKqYccN*@zQ|&r=O5@~B2Gn6~G+Nrg2W-bx3iJOq^LpHLFjtlmCm1s-Hhsr;xx&LUe@^(CCi3ER~e$J6H;f zb%XA7Q(jdlo}A1#9W?^J08K0XKvo=MHh}!iJy!j?YhXg%#ag4!k#dqZbPA-NGu<(6 zoP5DgZZv8!k`H`hk=RZP!NdBMR?nlLp-&bUjFX|GhfELnM+Dd3_CH7|)>4E92Bt#y zTa?Txha6Zl2Mx&5@iI)BYy6cpda2OiYa0Moz<`Q2Bnw6=stq9yB?A^@sNG9?FAW`< z&clwn9V_?2I&Z!R%ErcubI$rm60Bg6u4-bwW1(YVSHrKPuessO&C9P+mE_FhjGH}O z1{(cQ>h!D28Sj3!%PDWC&uMD&M7txwHwG_8Y<@>s&JuuzOMkD~0VgDtzyYTO#X#>{ zC){nWULdtTt9;rvdjfrHXc|?hoPM@88-r5l2<#jvbWu{od&&*!0)^-l)=-Qz9BLCk zHpOS6YP`;To&oWa%p)M2wRyfZ)#vmfA=*MF=WL~79JV=eR`0oSiBOCT8j)(HaSP@d zb+vpRSq*?%F=P^bD`wf}!tA#jR|20Cam#Sc2j$V{*hbi*d>ob=K5r%b)BEo(27FHsp(D$hl?u{_{0iG1rgYI45%>vTCk`dJR)EWwS#;oN?vrKO(?%QBS zcM7xv*a?;h-LV!R^I7?qagclM#FkrjiY#;x;3dt{*GXwgk^7BTmvt&*nto6&tF{T+ zOJyykk@7>CT#ZU}Su(6I*2Z|Bzz4slG5q{h5UufX3?EIFH`m>R#M zjdpU4od%07|M0khczVH`JY`c4KgYPWM90*3T+?p^bDZ?`a6c60la~P)k^A4Qh;n5e zd;p*z!t6vrRwYhfzqzfGj;g%cS41LgYg(Fei&T#9q?;yytwa_N`r%2!YoqXSU8jy+`iY5!$BQ|6R5kE4H7uzL+x5kalH@T@HFhdC=ZDQ=3p_zCc5S{DAUN=Zqb|4;{?rI# zz8!d&r*nPMFcR;sFNmkEiKH_d|JKLZfDTm_EdZ+=>6TJcWe7}Use5%cP?KG$85zJi zUmxKFldqE9#BUn56k~mw^D8IY6)sx34{dIOn1n0^PA$eg6A{~VoB;B^^%j2GycM8hS4_Lj3))CEHS_PLYAn?0VG%1ur|BLZ?Po zn(9*H%qc~?9y+X>qgotctkm2YUkQMDuy>hEu(E}W&)HV*nslplU$ULG;h`vzD&T2&$*+q zdztoy{n28_hpg#4aD2u&dh526GV#Ioo<~R~8nxmoGdODP%rzznHt`uDm*EoUIYbWb zm0InoOk8%zKFXj1aoXyKOhNVso_)G;gwSr@TmhNLv=Z?xDWYe! z5EKep&J4!`LI@sP_6P>b1wMcaN@L9ptW-#2yVd^qw{lepYgiqD)o}1ILA&5=X%f+r zayQb{+zB=sE?K$WlPk0~d8IAY+~1+)?2|G)>$RQhj~l;ZwI;L7`~pQ*SFkT>qo-%Q z&s-%fC2*a&V$(cvquy$%bDl{?Xij?XoVsd1w07wwcbdSgU8i(|t|SY<8jwZDKqyJo zxs+(tLiHO;eEjZjT&%A(VvT9gD>N@2(K^gHGm-{jWtmcO{Ud)$dNvUzX?h2`Sej)v zzfQ_>A?VUfu7eDQfwz?4BL2Afd--Jzr2+7f@t&`Afcz3^yPYyDso@2NhoqG8zSuV^ zSr_Y0UF1@;AG|thnF|@9&EgX7rn&Jb?scm&Ctxm<>dS9s@kXtO|0G2ml1(s-ru|p> zVB)}T1frUC!PL7FP*?_Xh=Tmd?#}HEhWl9OP2gn`T5zg92f1=s~S8-T3ZJau{F46 z84!Yd5zvn91w!76gd|47FYYxZOp^_aB{4f*X**mp4A}cd40wF z4TtDu6W}N_unjs}pT8`l?5@|)a+a-rDXN%F)Y^s2;D^(i2Z zuj3=z56~+8lJy)S8z9_}Gsmi90@1H*cL?_9*gxdIzJGbgsC@>hCPbWJq`wTYVu{d;cP!}ioEIFD z%3lG9rllRz1oXtNVjip(XHQIdUVhb7PApkIDO^UJsv8#?bbA0B!L8j2~IYO|V#cgv)&%P_Y-pR%IS{&Zp~bhPdju|nhi8Q>l34Z$ZCfv;nhQ}ng~$3P&_ z8za?J!_Tvv&_Zygq~mZ_l=S^p61QpC`yD$z`NNOx(k-NDibR{z3h5jLiii8w8k5fc zO^YK~i%jdIh}rX&g#~_M$|t~_?CT%ofsyrALDX(!WRC%om-90=`u3Xm@P6=hEQ0cQ z@!kP(!vJZ6ePHP2f;0a-0TNYbabtPOuT=&r zE%At(4bFTs&Ycg$!+yHuoe^gFcL#wvKVXN$Eul3trwFWw5!N<+@0*(NY6lO)`4pjb z)jm;ZV?JsQC7fOJd89iHUB*horyu(U!&c(@s0pAn$(G_B6RgC)YHM*fXjJWywr7!` zmy^UzrtsCyHd=t=f$W^&ZOD}@7?)`>h)xjT2HEq&ho${< zMc`QqD$?!%OJX%_^M)T{0VK?KLe61qU*ZCQjX<)$5_dmAe92SxQ=omc;Ku|(oP|Q| z0dCi!p3EDMolFRxuq1!vh>4T_V7%&$m6!;MmTX8MN0)1-#rJM|`KlYfB&b3f^@h-m z^Pj1YbYnh5=}c5J7-O#4#cn=mg&0S-o@%bZoW9v08WK0u&&XueFnt2CKMkl5BCOj1 z!?vBbIK{h@8z#vN4#rZIg#Mr6KbTm`l8dx9*TGO?sDCn;k8zgJTaSUCQ!2f<_ggC! z_~_foY{jI}wPx+@9aO^COrq6V@dIUVn(jr_9dkY{oop;vnw`?wyT`6|dd?>wA?kBf zVR?r3ndpU~H!YbO@lteX6+1g!8*BYGz>1KRE<0NSYNZipLOd-}a@LqobKAM;2=yEE zaWCe9IJ$keWN{Bw|C+f#B%>#h);7#yB=HX2Q)CjcA^uCW`BlC|Xjh8UA`|))U5F)6 z^*A1(Rt*m|QDKpiA0@%PVFal~_f%4yNK8Ka`&rK-BXho*{y^g;lYNrG4%NLNz&=@c z+x;HlG@iYMo;vLCvvDW>{p?6o^$uzivLRbXfEa;bB&zp#u6atm><0qApf-gr?hD2= zv>xMWB2lKsxpy1oq0Xjf=AM^C?oW8{PFOzg(XHyvXMFuRxN0^vXDqkj*~Te~$;cNU0KReQcdl#~oOA31f+OirI9|}Z(An(g?EyU{ECcU6 zwvMfK6!U@$X0*HJza^;6o={+AndAG;IDB@+A;AY1y^+8DPZZLaUl*Z zuRxZSMhNaCyW$SRRN1D3p$BUtx?;G~m5t)B`#2jFenp8gghT5+|HCd`_q%OhD zbo}GFw1T;;vihVtWy>2;iW$djl zkOMON{WKUqgOR7Y8w)+t-vhqDO`uYMg0>cvnLz7U# zuQAN2P#X`ruAxL*lODiK(1SNt#Vy=S2B>_b)SLgViVzdnZ(mU$@{=y{Mo&=_by z_lW1)_wTAaC4RmqaycUJKLWVyQIP*c)JH|HM)Ok|ly0Z0$JGSLorf~?WK2gkn{uzG zJbOy%_r!fyXs16zAq7f5kfiL`g+#TaFIE}zr}_xTb{Y}juRaU42NAQy3X7upi+|BO zih-}2QX~9+w}rGMdP)w$Psl<(aFyrd>R%@=JSEkMa6uz%lP zi#e}nCJvKl%vzS3i`_S^-{bOSL}pawz^<%%CZhWc(Z?GAMej4&JGrcVrmy}2-F{E6 zl}y-Ed=-h<7ZF*}&wfGTOpoK9!L45U-aiVj70d1VCECf|jF?JMi$s+8@xToDLa5jo zoxeeMP>Xw|!MQ$ww!S0x*@;kSwzf8WS-ocb3HFzw(j%|ps(S_M3&gL_P7ahWQPh>n z_O$*n8pPIhnSHhrk*gad>yZprZQVuhxP(~&U2sHEyfgZk+vr;vKcVE?I_K-lU z6q>|LmCF(1Bu36H>7Ue6J1A+rV-&pQpVt9=bAMv4+Fxe8Lw~-t$nPHLEmKN6q*l9~ z!)Z8t92Qert2iqsh;^8}O^zyYtn8>>{Z3fyGC9WmPUS}jU-p;kSnAVqKLNkw&Y_hd z9*CR{K%!z^B&py%MX^8PgcZiYodt>#;bFlifcz}9#_=3@d4Z$g(peT8btT>T=C9dt z(-(9`{K3qbUkSjh`<)8@5ft}g2O$~3CF^T=$8pQ!gMP(z)>EdI4--b#{s%Pff-r4o z7klcwE@_N&%TiZQHI&^$z)#`cBL{ z!!omutKBkMVxO-wi3eVwZ@B9^BaV8OHb(uKAK$O?wj@8>6?SGl?@Kr9w%@&XIQ{j1 zhkyTG?X71xMR*2z#hU;%nwbGd1_WBT{^rc1b@H&vOaWAfRJ(Nya+4IxV6@4Ty}@80 zVSb9agWxO%AQQi-2bP$P!uv|yGY^Hzg)+~GPqUD+7dHw3|J(PS*E!07d#D^2bN5BF zFzC+F_WjXEp?$xcTW9xQUS}trU_9Z%5>tUtm^sm8A49AuoHY83F^m8f7nz;{)!S++ z>iqo7vzs$5nnBnXiJt8h+w62g_!5Lz`HK-+*5|x>Y8Ur-zTnl8=b#Yjk<-T`DjeJN zbRBpS_*i6jpF9DAfezuf+#&tV!Mn{i=Y)FbBWLg*&pzMzLoO5?e6!)a1J}pq6UK-m zju<$i^RF;0;dIE*j~eet9}l zu}Gh)KNNy7bQ~QndYtAJOlEyBXEWbG_hxb^l&omw70ke6ukXk8+TKJ-u-SK!TpwH> z*X(#Ym+2`U?%CxRf|1S5WVK+#p+zp8Z?WFL+S2>NBIRp8o4g5#PWJ*|>#(vxI4&zN zpKl1mXy}#0P+XE!>`Y^YZkwQmjnSqR^PD)Rf_K4=SykK42n=Plk(|h!aVger%b0t+ zFCa>zp81)?u!-K1;X;wf{5`M#%pPTqVlMplN>PTBcGcL+7?=Mtsn7iABy*d+9m!A4oMHcjJ6yiX}BMU~|swZcv$Dp=CC zd=WY3!oHKhN((;44$(rbAcgjF(*WN;MZ*hQ$1S*BTA?eOc@bWh9x)bkRDKvGU2E(K zjy<&`#aW+Xomn8~&EDdsxQe6e3RZ`{*2Ir{<0;NQdd@F^L$$SvQ>;Cm5<(gDvC#S` ziII6v8I3sY^k`Oc9qf62c1`b5Z5B3tbnZubdg(j=#e9Iz^TZYM^AlBmiE1G%b!@I+Z#Z|J8gx++Ns1t|jfwfQ|d9w4spI<*cNOUvb9jtf4y~H_kT3ljH z9F^io3msemIQgtFo!R^0)Z_#9;O`*1t&pV7+0nj;Ov?xgkHjuaMnPZ_d-7X)3ct1H zi4KA%TD;@S965{%G(;l)2E$9u(H@ZFqHTEx<^S0Tu2U7IEoWnN@kV><+~!HL&tVBA zqgBgEtmo|92P$keofqY3klJLoIk{fTzbN7>+c$P>0CRmjr$9&v=My-$m#`fxzh@qW z37-9yLToG(k_GE$Bgo=V;q9hKemo)Y#IMnqG@g%)#Er!$ZZ5U#vcgp ztF-6qOlWt|3X}*@a7W5K<$CDsZ9MU5k!)H%$k;=<948DPs;@G%L104eIE!bjOSp9%arW)~eO1LN4B~|Jq3y6=OlV8Z54-*<7 zmoe$I@@C;Y3)wx6`UKgoGCw@ECn(qJO8lymRJt(L`XJfg# zKH%Pm=)4!Yv7CsYBv2{}WyghAJTxH&3^$#p0^DJd@^|uboEQK8 zARLI-5to~D36?8Q3Y*MdO{JMjF^`g_FLM4?6r%^D{Pf)#3OPnOt>js5u?6O*z>Dv6 z?ldj2GZ@yGPe}aK^U6|L4*|+&YgB8#C-L{Mc%^fy<-Ou$4+lk zK-8&3NwEgg>Ak}y>Iu7(GEEHc;gM`t*^b+E3VngiPBMT9d_yn*_fD3WDl$+liL)as zOGUPXwwb&^u_rf@6?2D<>Z8wrx|3ud0A24HKkFA|w%9GOVJmtIXu4zdKtAb>_o@6P zI_1zX5V4egk&~AY%{T_5M>HDtg0W#ei6!vuK!aH50ngkH9|20x#F$0=8Qp2t2QmEm z^G#37!J*lA(&r2=j@r`pZ@Yc;#WKvq=$VhZT#qoD-;h$U1i5tGG0y1SY_n2-=KtT@m zat?GPZDmoEjBlVEdrWxfFoR3t05Ullgb)**-dJNOTNrx2$cy2^$23Mt8!+zBwA3-} zQ4ool_JJqNR`pHB8Jvm~i~7h0Qar9ER?lmjNX-0W-?fr-@mtrGs+NaLv2`${s7q%g z!+Hbq&rI|$JbX@Fd%hLbZDaFWr8*&XwJ8tXoW_vl>~gQ?F_AN)a?@0Z13YG)Z@|_n z@s(Z(+vX=(*Cufji#Ev1e4su66e7FfWf$;GM`>@WMDEenvXkux2)?0V6ZTv4!ih`4 z6xyKDcue(CeNP?noHwgeVnYa)0N9-t%gb8QguW-K3mO>@2L$$kjyodS~|7BhlzE_*x@ru!Ni6YQr zT%Y$6gEmGw$3}%34y}pz`&#{meOSATG+6ID_nkXk8wwlp8Bi*NX&6JgA~zBK znkE2I=W*+;I^iKe!dNz-k1^d}KpI)~(!JBarLyq*6^(xH9K-&#a}3%({yX7-J{h}N z*_tvso0*tdIl3}AIy*R;IlEe!xzNio(v6Q!{G=aKkY%jnoMPvgWQPXr!ok2kk92H< zbVir>=szR? z3>-TvlZma7i_8BOxZrHsfehNSe+N2(&+&>I4D%P4^j`%Y!R^^Lf~3si2*JQeUy*j< z{vyF5y($lQK1d2GVF~9ymw`WV{(bkf-i1BwATQ+zM3Bqht9*xW{}5MjN&Z!tteYW7 zZ_pitg7SaeWb-5-5)0ra#TDZJ9t7F{K9s zBY8y%;r@#R^FPg|GV;3)7jl0t6Urj~E6=~;8eU)dH;vbUGC=zg99_L@gR}r*c z5sQ@nBI5k>j$cJBd4&hl{)1=MBm`EELj9BTUwQxkeKbrT{&3DmiT{cJTT>m>NHnj? zfH(XL4+B&kLkD$6pvBnlmH}7+KUz@!=aK#Hhht;#M8B!)R{z&U|LTV(wpRb?i@@tY zR3vNQTTAwTwaY7+(N}dyvjq`wf2)rEr##@-@!yqr?*w%6A^^UO6a20i!Ni|O<_r{> zVEO0Gz0v@Dm9dKJAHJS5BJkV9s|NgA{RxDB-5`11APzYYc@puTWxUd3dR0b@4+t*^ z1Wdll|CN%=D?B#eqg<|&eY*6Hul`kS`wY|S75 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae45383b..1b2b07cf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d5..af6708ff 100755 --- a/gradlew +++ b/gradlew @@ -28,7 +28,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" diff --git a/gradlew.bat b/gradlew.bat index e95643d6..0f8d5937 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/java-core/build.gradle b/java-core/build.gradle index 22bf8970..e7b40bb9 100644 --- a/java-core/build.gradle +++ b/java-core/build.gradle @@ -7,7 +7,8 @@ ext { dependencies { compile project(":core") - testCompile dependencyJunit + testCompile junitCompile + testRuntime junitRuntime } performSigning(signingEnabled, signModule) diff --git a/performance/build.gradle b/performance/build.gradle index cac2f584..c6f4c8a6 100644 --- a/performance/build.gradle +++ b/performance/build.gradle @@ -1,5 +1,6 @@ dependencies { compile project(":core") - testCompile dependencyJunit + testCompile junitCompile + testRuntime junitRuntime } diff --git a/props-core-scalacheck/build.gradle b/props-core-scalacheck/build.gradle index 10e494c9..5207bf50 100644 --- a/props-core-scalacheck/build.gradle +++ b/props-core-scalacheck/build.gradle @@ -15,7 +15,8 @@ dependencies { compile "org.scala-lang:scala-library:$scalaVersion" compile "org.scalacheck:scalacheck_$scalacheckScalaVersion:$scalacheckVersion" - testCompile dependencyJunit + testCompile junitCompile + testRuntime junitRuntime } performSigning(signingEnabled, signModule) diff --git a/props-core/build.gradle b/props-core/build.gradle index 181ae5b2..b991bd47 100644 --- a/props-core/build.gradle +++ b/props-core/build.gradle @@ -2,6 +2,7 @@ archivesBaseName = "${project.projectName}-${project.name}" dependencies { - compile project(":quickcheck") - testCompile dependencyJunit + compile project(":quickcheck") + testCompile junitCompile + testRuntime junitRuntime } diff --git a/quickcheck/build.gradle b/quickcheck/build.gradle index b477e26a..2e5dbe4c 100644 --- a/quickcheck/build.gradle +++ b/quickcheck/build.gradle @@ -7,7 +7,7 @@ archivesBaseName = "${project.projectName}-${project.name}" dependencies { compile project(":core") - compile dependencyJunit + compile junitCompile } performSigning(signingEnabled, signModule) From c718d06f8320b56084b507b3d5b2d13a6f51ac3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Wed, 13 Mar 2019 18:00:24 -0400 Subject: [PATCH 072/173] Correct Either.iif Javadoc --- core/src/main/java/fj/data/Either.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/fj/data/Either.java b/core/src/main/java/fj/data/Either.java index a9a746a9..dcc950f2 100644 --- a/core/src/main/java/fj/data/Either.java +++ b/core/src/main/java/fj/data/Either.java @@ -898,7 +898,7 @@ public static A reduce(final Either e) { } /** - * If the condition satisfies, return the given A in left, otherwise, return the given B in right. + * If the condition satisfies, return the given B in right, otherwise, return the given A in left. * * @param c The condition to test. * @param right The right value to use if the condition satisfies. From 68625ac4f1a40c65b2fb0ad4c24cacbe9288e6bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Fri, 15 Mar 2019 22:17:33 -0400 Subject: [PATCH 073/173] Add Integers tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- .../test/java/fj/function/IntegersTest.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 core/src/test/java/fj/function/IntegersTest.java diff --git a/core/src/test/java/fj/function/IntegersTest.java b/core/src/test/java/fj/function/IntegersTest.java new file mode 100644 index 00000000..985afba9 --- /dev/null +++ b/core/src/test/java/fj/function/IntegersTest.java @@ -0,0 +1,62 @@ +package fj.function; + +import org.junit.Test; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import static fj.data.List.list; +import static fj.data.Option.none; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.*; + +public class IntegersTest { + + @Test + public void testSum() { + assertThat(Integers.sum(list(3, 4, 5)), is(12)); + } + + @Test + public void testProduct() { + assertThat(Integers.product(list(3, 4, 5)), is(60)); + } + + @Test + public void testAdd() { + assertThat(Integers.add.f(10).f(20), is(30)); + } + + @Test + public void testMultiply() { + assertThat(Integers.multiply.f(3).f(5), is(15)); + } + + @Test + public void testAbs() { + assertThat(Integers.abs.f(-5), is(5)); + } + + @Test + public void testFromString() { + assertThat(Integers.fromString().f("-123").some(), is(-123)); + } + + @Test + public void testFromStringFail() { + assertThat(Integers.fromString().f("w"), is(none())); + } + + @Test + public void testCannotInstantiate() throws NoSuchMethodException, IllegalAccessException, InstantiationException { + Constructor constructor = Integers.class.getDeclaredConstructor(); + constructor.setAccessible(true); + try { + constructor.newInstance(); + fail("expected InvocationTargetException"); + } catch (InvocationTargetException ite) { + assertTrue(ite.getCause() instanceof UnsupportedOperationException); + } + } + +} From d2319f14e9b7b3293532454e110f6f0f675b26db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sat, 6 Apr 2019 15:22:51 -0400 Subject: [PATCH 074/173] Add openjdk12 to Travis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bbe5966b..8e7c52c9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,12 +5,13 @@ language: java jdk: - openjdk10 + - openjdk11 - openjdk-ea matrix: include: - - jdk: openjdk11 + - jdk: openjdk12 script: - ./gradlew build coverage -s -i after_success: From 40ef6c620aade5633e720f0401991bcfd9f18745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sat, 6 Apr 2019 16:42:41 -0400 Subject: [PATCH 075/173] Add Longs tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- core/src/main/java/fj/function/Longs.java | 39 ++++++++++++ core/src/test/java/fj/function/LongsTest.java | 62 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 core/src/test/java/fj/function/LongsTest.java diff --git a/core/src/main/java/fj/function/Longs.java b/core/src/main/java/fj/function/Longs.java index 18667699..0989e8c0 100644 --- a/core/src/main/java/fj/function/Longs.java +++ b/core/src/main/java/fj/function/Longs.java @@ -1,11 +1,16 @@ package fj.function; import fj.F; +import fj.Monoid; +import fj.data.List; +import fj.data.Option; import static fj.Function.curry; import static fj.Semigroup.longAdditionSemigroup; import static fj.Semigroup.longMultiplicationSemigroup; +import static fj.data.Option.none; +import static fj.data.Option.some; import static java.lang.Math.abs; /** @@ -48,4 +53,38 @@ private Longs() { */ public static final F> remainder = curry((a, b) -> a % b); + /** + * Sums a list of longs. + * + * @param longs A list of longs to sum. + * @return The sum of the longs in the list. + */ + public static long sum(final List longs) { + return Monoid.longAdditionMonoid.sumLeft(longs); + } + + /** + * Returns the product of a list of integers. + * + * @param longs A list of longs to multiply together. + * @return The product of the longs in the list. + */ + public static long product(final List longs) { + return Monoid.longMultiplicationMonoid.sumLeft(longs); + } + + /** + * A function that converts strings to integers. + * + * @return A function that converts strings to integers. + */ + public static F> fromString() { + return s -> { + try { return some(Long.valueOf(s)); } + catch (final NumberFormatException ignored) { + return none(); + } + }; + } + } diff --git a/core/src/test/java/fj/function/LongsTest.java b/core/src/test/java/fj/function/LongsTest.java new file mode 100644 index 00000000..d59fc07c --- /dev/null +++ b/core/src/test/java/fj/function/LongsTest.java @@ -0,0 +1,62 @@ +package fj.function; + +import org.junit.Test; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import static fj.data.List.list; +import static fj.data.Option.none; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.*; + +public class LongsTest { + + @Test + public void testSum() { + assertThat(Longs.sum(list(3L, 4L, 5L)), is(12L)); + } + + @Test + public void testProduct() { + assertThat(Longs.product(list(3L, 4L, 5L)), is(60L)); + } + + @Test + public void testAdd() { + assertThat(Longs.add.f(10L).f(20L), is(30L)); + } + + @Test + public void testMultiply() { + assertThat(Longs.multiply.f(3L).f(5L), is(15L)); + } + + @Test + public void testAbs() { + assertThat(Longs.abs.f(-5L), is(5L)); + } + + @Test + public void testFromString() { + assertThat(Longs.fromString().f("-123").some(), is(-123L)); + } + + @Test + public void testFromStringFail() { + assertThat(Longs.fromString().f("w"), is(none())); + } + + @Test + public void testCannotInstantiate() throws NoSuchMethodException, IllegalAccessException, InstantiationException { + Constructor constructor = Longs.class.getDeclaredConstructor(); + constructor.setAccessible(true); + try { + constructor.newInstance(); + fail("expected InvocationTargetException"); + } catch (InvocationTargetException ite) { + assertTrue(ite.getCause() instanceof UnsupportedOperationException); + } + } + +} From 0b8b7f083ce5cac932467e3a839357a568ef6dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sat, 12 Oct 2019 13:25:44 -0400 Subject: [PATCH 076/173] Bring Gradle to 5.6.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- build.gradle | 6 +++--- core/build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 55190 -> 55616 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 22 +++++++++++++++++++--- gradlew.bat | 18 +++++++++++++++++- 6 files changed, 41 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index b206336a..69c01425 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } wrapper { - gradleVersion = "5.2.1" + gradleVersion = "5.6.2" distributionType = Wrapper.DistributionType.ALL } } @@ -49,7 +49,7 @@ allprojects { snapshotAppendix = "-SNAPSHOT" fjVersion = fjBaseVersion + (isSnapshot ? snapshotAppendix : "") - fjConsumeVersion = "4.8" + fjConsumeVersion = "4.8.1" signModule = false @@ -70,7 +70,7 @@ allprojects { primaryEmail = "functionaljava@googlegroups.com" junitCompile = "junit:junit:4.12" - junitRuntime = "org.junit.vintage:junit-vintage-engine:5.3.2" + junitRuntime = "org.junit.vintage:junit-vintage-engine:5.5.2" displayCompilerWarnings = true } diff --git a/core/build.gradle b/core/build.gradle index 767904e0..cbdad60b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -8,7 +8,7 @@ archivesBaseName = project.projectName dependencies { testCompile junitCompile testRuntime junitRuntime - testCompile 'com.h2database:h2:1.4.197' + testCompile 'com.h2database:h2:1.4.199' testCompile 'commons-dbutils:commons-dbutils:1.7' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 87b738cbd051603d91cc39de6cb000dd98fe6b02..5c2d1cf016b3885f6930543d57b744ea8c220a1a 100644 GIT binary patch delta 3320 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 Date: Sun, 20 Oct 2019 17:28:54 +0300 Subject: [PATCH 077/173] define base bounded interface --- core/src/main/java/fj/Bounded.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 core/src/main/java/fj/Bounded.java diff --git a/core/src/main/java/fj/Bounded.java b/core/src/main/java/fj/Bounded.java new file mode 100644 index 00000000..aea5da25 --- /dev/null +++ b/core/src/main/java/fj/Bounded.java @@ -0,0 +1,10 @@ +package fj; + +public class Bounded { + + public interface Definition { + A min(); + + A max(); + } +} From 78b3f7600f102b2cee62e0a3a300b38131fc3b5e Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 20 Oct 2019 17:38:53 +0300 Subject: [PATCH 078/173] implement bounded with factory methods --- core/src/main/java/fj/Bounded.java | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/core/src/main/java/fj/Bounded.java b/core/src/main/java/fj/Bounded.java index aea5da25..9c64a0b7 100644 --- a/core/src/main/java/fj/Bounded.java +++ b/core/src/main/java/fj/Bounded.java @@ -2,9 +2,41 @@ public class Bounded { + private final Definition def; + public interface Definition { A min(); A max(); } + + private Bounded(Definition definition) { + this.def = definition; + } + + public A min() { + return def.min(); + } + + public A max() { + return def.max(); + } + + public static Bounded boundedDef(Definition def) { + return new Bounded<>(def); + } + + public static Bounded bounded(A min, A max) { + return boundedDef(new Definition() { + @Override + public A min() { + return min; + } + + @Override + public A max() { + return max; + } + }); + } } From 0083c800a32140c7fcdb6dadb793f0cdd07b8dba Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 20 Oct 2019 17:46:40 +0300 Subject: [PATCH 079/173] add bounded for integers --- core/src/main/java/fj/Bounded.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/fj/Bounded.java b/core/src/main/java/fj/Bounded.java index 9c64a0b7..57827589 100644 --- a/core/src/main/java/fj/Bounded.java +++ b/core/src/main/java/fj/Bounded.java @@ -39,4 +39,7 @@ public A max() { } }); } + + public static final Bounded integerBounded = bounded(Integer.MIN_VALUE, Integer.MAX_VALUE); + } From 68828d3b8d72c8a481974902090f36b80e12bbd8 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 20 Oct 2019 18:49:01 +0300 Subject: [PATCH 080/173] create tests for set monoid fabric methods --- core/src/test/java/fj/MonoidTest.java | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/core/src/test/java/fj/MonoidTest.java b/core/src/test/java/fj/MonoidTest.java index 8bab5315..99ad5b5e 100644 --- a/core/src/test/java/fj/MonoidTest.java +++ b/core/src/test/java/fj/MonoidTest.java @@ -1,6 +1,8 @@ package fj; +import fj.data.Enumerator; import fj.data.Option; +import fj.data.Set; import fj.data.Stream; import org.junit.Test; @@ -16,4 +18,26 @@ public void lifted_sum_of_two_numbers() { assertThat(optionMonoid.sum(some(3), some(5)), is(some(8))); assertThat(optionMonoid.sumLeft(Stream.arrayStream(some(3), some(5))), is(some(8))); } + + @Test + public void intersection_monoid_test() { + Bounded integersBounded = Bounded.bounded(0, 10); + Monoid> intersectionMonoid = Monoid.setIntersectionMonoid(integersBounded, Enumerator.intEnumerator, Ord.intOrd); + Set first = Set.set(Ord.intOrd, 1, 2, 3, 4); + Set second = Set.set(Ord.intOrd, 3, 4, 5, 6); + Set zero = intersectionMonoid.zero(); + Set actual = intersectionMonoid.sum(intersectionMonoid.sum(zero, first), second); + assertThat(actual, is(Set.set(Ord.intOrd, 3, 4))); + } + + @Test + public void union_monoid_test() { + Monoid> intersectionMonoid = Monoid.setMonoid(Ord.intOrd); + Set first = Set.set(Ord.intOrd, 1, 2, 3, 4); + Set second = Set.set(Ord.intOrd, 3, 4, 5, 6); + Set zero = intersectionMonoid.zero(); + Set actual = intersectionMonoid.sum(intersectionMonoid.sum(zero, first), second); + assertThat(actual, is(Set.set(Ord.intOrd, 1, 2, 3, 4, 5, 6))); + } + } From 73c31ac71a0605cf36936396f5e3d3646df37abc Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 20 Oct 2019 18:58:16 +0300 Subject: [PATCH 081/173] implement to stream with bounded function in enumerator --- core/src/main/java/fj/data/Enumerator.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/fj/data/Enumerator.java b/core/src/main/java/fj/data/Enumerator.java index 94f1b2cd..85b8377f 100644 --- a/core/src/main/java/fj/data/Enumerator.java +++ b/core/src/main/java/fj/data/Enumerator.java @@ -1,16 +1,12 @@ package fj.data; -import fj.F; +import fj.*; import static fj.Function.*; import static fj.data.Option.none; import static fj.data.Option.some; -import fj.Function; -import fj.Ord; - import static fj.Ord.*; -import fj.Ordering; import static fj.Ordering.*; import java.math.BigDecimal; @@ -185,6 +181,18 @@ public Stream toStream(final A a) { return Stream.fromFunction(this, id, a); } + /** + * Returns a stream of the values from this enumerator, + * starting at the min of given Bounded, ending at the max, counting up. + * + * @param bounded A value at which to begin the stream. + * @return a stream of the values from this enumerator, cut by bounded, counting up. + */ + public Stream toStream(final Bounded bounded) { + final F id = identity(); + return Stream.fromFunction(this, id, bounded.min()).takeWhile(item -> !item.equals(bounded.max())); + } + /** * Create a new enumerator with the given minimum value. * From fce0df7b4f6fdc01489536c08da9906d406296ac Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 20 Oct 2019 18:59:38 +0300 Subject: [PATCH 082/173] implement intersection monoid for sets --- core/src/main/java/fj/Monoid.java | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index 0c09b558..e66a812f 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -2,14 +2,7 @@ import static fj.F1Functions.dimap; -import fj.data.Array; -import fj.data.DList; -import fj.data.List; -import fj.data.IO; -import fj.data.Natural; -import fj.data.Option; -import fj.data.Set; -import fj.data.Stream; +import fj.data.*; import static fj.Function.*; import static fj.Semigroup.semigroupDef; @@ -1109,6 +1102,20 @@ public Set append(Set a1, Set a2) { }); } + public static Monoid> setIntersectionMonoid(final Bounded bounded, final Enumerator enumerator, final Ord o) { + return monoidDef(new Definition>() { + @Override + public Set empty() { + return Set.iteratorSet(o, enumerator.toStream(bounded).iterator()); + } + + @Override + public Set append(Set a1, Set a2) { + return a1.intersect(a2); + } + }); + } + /** * A monoid for the maximum of elements with ordering o. * @deprecated since 4.7. Use {@link Ord#maxMonoid(Object)} From b550ca45e4bed28153bac587201ac570118df638 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 20 Oct 2019 19:05:28 +0300 Subject: [PATCH 083/173] fix set monoid method docs --- core/src/main/java/fj/Monoid.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index e66a812f..02bba94a 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -1083,7 +1083,7 @@ public Unit append(Unit a1, Unit a2) { }); /** - * A monoid for sets. + * A union monoid for sets. * * @param o An order for set elements. * @return A monoid for sets whose elements have the given order. From 3c4e35fb9cb32ab3584cdf49fde27fe7d8987049 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 20 Oct 2019 19:05:49 +0300 Subject: [PATCH 084/173] add docs for intersection set monoid --- core/src/main/java/fj/Monoid.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index 02bba94a..2b3400c0 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -1102,6 +1102,14 @@ public Set append(Set a1, Set a2) { }); } + /** + * A intersection monoid for sets. + * + * @param bounded A bound for all possible elements + * @param enumerator An enumerator for all possible elements + * @param o An order for set elements. + * @return A monoid for sets whose elements have the given order. + */ public static Monoid> setIntersectionMonoid(final Bounded bounded, final Enumerator enumerator, final Ord o) { return monoidDef(new Definition>() { @Override From b602a2c2c598615b7ceb305d6ca79253449a36e5 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 20 Oct 2019 19:50:53 +0300 Subject: [PATCH 085/173] add docs for bounded --- core/src/main/java/fj/Bounded.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/main/java/fj/Bounded.java b/core/src/main/java/fj/Bounded.java index 57827589..22d59ef5 100644 --- a/core/src/main/java/fj/Bounded.java +++ b/core/src/main/java/fj/Bounded.java @@ -1,9 +1,16 @@ package fj; +/** + * The Bounded class is used to name the upper and lower limits of a type. + * Ord is not a superclass of Bounded since types that are not totally ordered may also have upper and lower bounds. + */ public class Bounded { private final Definition def; + /** + * Minimal definition of Bounded + */ public interface Definition { A min(); From ea6fcc7127219874ba550005d3de20ea76d789b3 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 20 Oct 2019 19:51:11 +0300 Subject: [PATCH 086/173] remove integer bounded implementation --- core/src/main/java/fj/Bounded.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/main/java/fj/Bounded.java b/core/src/main/java/fj/Bounded.java index 22d59ef5..980ba9a3 100644 --- a/core/src/main/java/fj/Bounded.java +++ b/core/src/main/java/fj/Bounded.java @@ -47,6 +47,4 @@ public A max() { }); } - public static final Bounded integerBounded = bounded(Integer.MIN_VALUE, Integer.MAX_VALUE); - } From 4b25bf8797258361c8ce0624a1f9f4a2721e3384 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 20 Oct 2019 20:01:36 +0300 Subject: [PATCH 087/173] rename monoid variable in union monoid test --- core/src/test/java/fj/MonoidTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/fj/MonoidTest.java b/core/src/test/java/fj/MonoidTest.java index 99ad5b5e..35e71c28 100644 --- a/core/src/test/java/fj/MonoidTest.java +++ b/core/src/test/java/fj/MonoidTest.java @@ -32,11 +32,11 @@ public void intersection_monoid_test() { @Test public void union_monoid_test() { - Monoid> intersectionMonoid = Monoid.setMonoid(Ord.intOrd); + Monoid> unionMonoid = Monoid.setMonoid(Ord.intOrd); Set first = Set.set(Ord.intOrd, 1, 2, 3, 4); Set second = Set.set(Ord.intOrd, 3, 4, 5, 6); - Set zero = intersectionMonoid.zero(); - Set actual = intersectionMonoid.sum(intersectionMonoid.sum(zero, first), second); + Set zero = unionMonoid.zero(); + Set actual = unionMonoid.sum(unionMonoid.sum(zero, first), second); assertThat(actual, is(Set.set(Ord.intOrd, 1, 2, 3, 4, 5, 6))); } From 606f1e72803937b578e03aaca3f2c4dfd99c7c4c Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 20 Oct 2019 20:01:59 +0300 Subject: [PATCH 088/173] add tests for set semigroup --- core/src/test/java/fj/SemigroupTest.java | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 core/src/test/java/fj/SemigroupTest.java diff --git a/core/src/test/java/fj/SemigroupTest.java b/core/src/test/java/fj/SemigroupTest.java new file mode 100644 index 00000000..20ecb2cb --- /dev/null +++ b/core/src/test/java/fj/SemigroupTest.java @@ -0,0 +1,26 @@ +package fj; + +import fj.data.Set; +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class SemigroupTest { + + @Test + public void intersection_semigroup_test() { + Semigroup> intersectionSemigroup = Semigroup.setIntersectionSemigroup(); + Set first = Set.set(Ord.intOrd, 1, 2, 3, 4); + Set second = Set.set(Ord.intOrd, 3, 4, 5, 6); + assertThat(intersectionSemigroup.sum(first, second), is(Set.set(Ord.intOrd, 3, 4))); + } + + @Test + public void union_semigroup_test() { + Semigroup> unionSemigroup = Semigroup.setSemigroup(); + Set first = Set.set(Ord.intOrd, 1, 2, 3, 4); + Set second = Set.set(Ord.intOrd, 3, 4, 5, 6); + assertThat(unionSemigroup.sum(first, second), is(Set.set(Ord.intOrd, 1, 2, 3, 4, 5, 6))); + } +} From b710672ec4586752c6d3265e3c8b80994d860ff1 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 20 Oct 2019 20:02:16 +0300 Subject: [PATCH 089/173] implement intersection semigroup --- core/src/main/java/fj/Semigroup.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index 42f05f5b..c512e383 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -629,4 +629,8 @@ public static Semigroup> setSemigroup() { return semigroupDef(Set::union); } + public static Semigroup> setIntersectionSemigroup() { + return semigroupDef(Set::intersect); + } + } From a6a0d21d6accc1e3b7b19f142e1f492b40845f1c Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Mon, 21 Oct 2019 19:25:49 +0300 Subject: [PATCH 090/173] add tests for zero elements of monoids --- core/src/test/java/fj/MonoidTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/core/src/test/java/fj/MonoidTest.java b/core/src/test/java/fj/MonoidTest.java index 35e71c28..721903b4 100644 --- a/core/src/test/java/fj/MonoidTest.java +++ b/core/src/test/java/fj/MonoidTest.java @@ -40,4 +40,21 @@ public void union_monoid_test() { assertThat(actual, is(Set.set(Ord.intOrd, 1, 2, 3, 4, 5, 6))); } + @Test + public void intersection_monoid_zero_test() { + Bounded integersBounded = Bounded.bounded(0, 10); + Monoid> monoid = Monoid.setIntersectionMonoid(integersBounded, Enumerator.intEnumerator, Ord.intOrd); + Set set = Set.set(Ord.intOrd, 1, 2, 3, 4); + Set zero = monoid.zero(); + assertThat(monoid.sum(zero, set), is(set)); + } + + @Test + public void union_monoid_zero_test() { + Monoid> monoid = Monoid.setMonoid(Ord.intOrd); + Set set = Set.set(Ord.intOrd, 1, 2, 3, 4); + Set zero = monoid.zero(); + assertThat(monoid.sum(zero, set), is(set)); + } + } From 226c84a74316d7d425610899eed2d0ef22d49599 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Mon, 21 Oct 2019 19:27:41 +0300 Subject: [PATCH 091/173] remove zero from monoids tests --- core/src/test/java/fj/MonoidTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/fj/MonoidTest.java b/core/src/test/java/fj/MonoidTest.java index 721903b4..d61da72f 100644 --- a/core/src/test/java/fj/MonoidTest.java +++ b/core/src/test/java/fj/MonoidTest.java @@ -25,8 +25,7 @@ public void intersection_monoid_test() { Monoid> intersectionMonoid = Monoid.setIntersectionMonoid(integersBounded, Enumerator.intEnumerator, Ord.intOrd); Set first = Set.set(Ord.intOrd, 1, 2, 3, 4); Set second = Set.set(Ord.intOrd, 3, 4, 5, 6); - Set zero = intersectionMonoid.zero(); - Set actual = intersectionMonoid.sum(intersectionMonoid.sum(zero, first), second); + Set actual = intersectionMonoid.sum(first, second); assertThat(actual, is(Set.set(Ord.intOrd, 3, 4))); } @@ -35,8 +34,7 @@ public void union_monoid_test() { Monoid> unionMonoid = Monoid.setMonoid(Ord.intOrd); Set first = Set.set(Ord.intOrd, 1, 2, 3, 4); Set second = Set.set(Ord.intOrd, 3, 4, 5, 6); - Set zero = unionMonoid.zero(); - Set actual = unionMonoid.sum(unionMonoid.sum(zero, first), second); + Set actual = unionMonoid.sum(first, second); assertThat(actual, is(Set.set(Ord.intOrd, 1, 2, 3, 4, 5, 6))); } From 1a40c67f5502980a1c18fa4e54bc56a0ad9d8362 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Mon, 21 Oct 2019 19:29:31 +0300 Subject: [PATCH 092/173] add docs for semigroups --- core/src/main/java/fj/Semigroup.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index c512e383..ef8029b3 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -621,7 +621,7 @@ public static Semigroup> ioSemigroup(final Semigroup sa) { public static final Semigroup unitSemigroup = unitMonoid.semigroup(); /** - * A semigroup for sets. + * A union semigroup for sets. * * @return a semigroup for sets. */ @@ -629,6 +629,11 @@ public static Semigroup> setSemigroup() { return semigroupDef(Set::union); } + /** + * A intersection semigroup for sets. + * + * @return a semigroup for sets. + */ public static Semigroup> setIntersectionSemigroup() { return semigroupDef(Set::intersect); } From 854d1958fffa8dedc56db0a2a2650f591949d983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Neuville?= Date: Mon, 28 Oct 2019 17:44:16 +0100 Subject: [PATCH 093/173] Basic JPMS (modules) support through 'Automatic-Module-Name' --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 67d8cd15..91bd5433 100644 --- a/build.gradle +++ b/build.gradle @@ -181,6 +181,7 @@ configure(subprojects.findAll { it.name != "props-core" }) { buildCommand 'org.eclipse.pde.ManifestBuilder' buildCommand 'org.eclipse.pde.SchemaBuilder' } + instruction 'Automatic-Module-Name', "functionaljava${project.name == 'core' ? '' : "-$project.name"}" } // Output MANIFEST.MF statically so eclipse can see it for plugin development From 1fed6abe362ee3a3931b11947570dd76baad9986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Neuville?= Date: Tue, 29 Oct 2019 12:10:05 +0100 Subject: [PATCH 094/173] Fix previous commit --- build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 91bd5433..2ac9a9c0 100644 --- a/build.gradle +++ b/build.gradle @@ -169,9 +169,10 @@ configure(subprojects.findAll { it.name != "props-core" }) { instruction 'Signature-Version', project.fjVersion instruction 'Bundle-ActivationPolicy', 'lazy' instruction 'Bundle-Vendor', 'functionaljava.org' - if(project.name != "core") { - instruction 'Require-Bundle', 'org.functionaljava;bundle-version="'+project.fjBaseVersion+'"' - } + if(project.name != "core") { + instruction 'Require-Bundle', 'org.functionaljava;bundle-version="'+project.fjBaseVersion+'"' + } + instruction 'Automatic-Module-Name', "functionaljava${project.name == 'core' ? '' : "-$project.name"}" } } @@ -181,7 +182,6 @@ configure(subprojects.findAll { it.name != "props-core" }) { buildCommand 'org.eclipse.pde.ManifestBuilder' buildCommand 'org.eclipse.pde.SchemaBuilder' } - instruction 'Automatic-Module-Name', "functionaljava${project.name == 'core' ? '' : "-$project.name"}" } // Output MANIFEST.MF statically so eclipse can see it for plugin development From 57699342be52381724ac34cf2b867cbd1d4ff407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Neuville?= Date: Tue, 29 Oct 2019 12:29:34 +0100 Subject: [PATCH 095/173] Dash character isn't allowed in modules names --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2ac9a9c0..c9d8be40 100644 --- a/build.gradle +++ b/build.gradle @@ -172,7 +172,7 @@ configure(subprojects.findAll { it.name != "props-core" }) { if(project.name != "core") { instruction 'Require-Bundle', 'org.functionaljava;bundle-version="'+project.fjBaseVersion+'"' } - instruction 'Automatic-Module-Name', "functionaljava${project.name == 'core' ? '' : "-$project.name"}" + instruction 'Automatic-Module-Name', "functionaljava${project.name == 'core' ? '' : ".$project.name"}" } } From 6cadc81f9b651b90cfb3e2d79c99a3b70caddbe8 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 3 Nov 2019 15:58:10 +0300 Subject: [PATCH 096/173] remove ord parameter and provide it from enumerator --- core/src/main/java/fj/Monoid.java | 5 ++--- core/src/test/java/fj/MonoidTest.java | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index 2b3400c0..490dc9d6 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -1107,14 +1107,13 @@ public Set append(Set a1, Set a2) { * * @param bounded A bound for all possible elements * @param enumerator An enumerator for all possible elements - * @param o An order for set elements. * @return A monoid for sets whose elements have the given order. */ - public static Monoid> setIntersectionMonoid(final Bounded bounded, final Enumerator enumerator, final Ord o) { + public static Monoid> setIntersectionMonoid(final Bounded bounded, final Enumerator enumerator) { return monoidDef(new Definition>() { @Override public Set empty() { - return Set.iteratorSet(o, enumerator.toStream(bounded).iterator()); + return Set.iteratorSet(enumerator.order(), enumerator.toStream(bounded).iterator()); } @Override diff --git a/core/src/test/java/fj/MonoidTest.java b/core/src/test/java/fj/MonoidTest.java index d61da72f..f185a124 100644 --- a/core/src/test/java/fj/MonoidTest.java +++ b/core/src/test/java/fj/MonoidTest.java @@ -22,7 +22,7 @@ public void lifted_sum_of_two_numbers() { @Test public void intersection_monoid_test() { Bounded integersBounded = Bounded.bounded(0, 10); - Monoid> intersectionMonoid = Monoid.setIntersectionMonoid(integersBounded, Enumerator.intEnumerator, Ord.intOrd); + Monoid> intersectionMonoid = Monoid.setIntersectionMonoid(integersBounded, Enumerator.intEnumerator); Set first = Set.set(Ord.intOrd, 1, 2, 3, 4); Set second = Set.set(Ord.intOrd, 3, 4, 5, 6); Set actual = intersectionMonoid.sum(first, second); @@ -41,7 +41,7 @@ public void union_monoid_test() { @Test public void intersection_monoid_zero_test() { Bounded integersBounded = Bounded.bounded(0, 10); - Monoid> monoid = Monoid.setIntersectionMonoid(integersBounded, Enumerator.intEnumerator, Ord.intOrd); + Monoid> monoid = Monoid.setIntersectionMonoid(integersBounded, Enumerator.intEnumerator); Set set = Set.set(Ord.intOrd, 1, 2, 3, 4); Set zero = monoid.zero(); assertThat(monoid.sum(zero, set), is(set)); From fe19604e96855faaa6005aeda6b71c24bcb66a53 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 3 Nov 2019 16:03:31 +0300 Subject: [PATCH 097/173] fix missing max value of bounded --- core/src/main/java/fj/data/Enumerator.java | 2 +- core/src/test/java/fj/MonoidTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/fj/data/Enumerator.java b/core/src/main/java/fj/data/Enumerator.java index 85b8377f..2fa96a6f 100644 --- a/core/src/main/java/fj/data/Enumerator.java +++ b/core/src/main/java/fj/data/Enumerator.java @@ -190,7 +190,7 @@ public Stream toStream(final A a) { */ public Stream toStream(final Bounded bounded) { final F id = identity(); - return Stream.fromFunction(this, id, bounded.min()).takeWhile(item -> !item.equals(bounded.max())); + return Stream.fromFunction(this, id, bounded.min()).takeWhile(item -> order.isLessThanOrEqualTo(item, bounded.max())); } /** diff --git a/core/src/test/java/fj/MonoidTest.java b/core/src/test/java/fj/MonoidTest.java index f185a124..a54bf672 100644 --- a/core/src/test/java/fj/MonoidTest.java +++ b/core/src/test/java/fj/MonoidTest.java @@ -29,7 +29,7 @@ public void intersection_monoid_test() { assertThat(actual, is(Set.set(Ord.intOrd, 3, 4))); } - @Test + @Test public void union_monoid_test() { Monoid> unionMonoid = Monoid.setMonoid(Ord.intOrd); Set first = Set.set(Ord.intOrd, 1, 2, 3, 4); @@ -42,7 +42,7 @@ public void union_monoid_test() { public void intersection_monoid_zero_test() { Bounded integersBounded = Bounded.bounded(0, 10); Monoid> monoid = Monoid.setIntersectionMonoid(integersBounded, Enumerator.intEnumerator); - Set set = Set.set(Ord.intOrd, 1, 2, 3, 4); + Set set = Set.set(Ord.intOrd, 7, 8, 9, 10); Set zero = monoid.zero(); assertThat(monoid.sum(zero, set), is(set)); } From 5b6fafccb10c433f52dbcc0aff375e0aef89aaa3 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 3 Nov 2019 16:04:52 +0300 Subject: [PATCH 098/173] make bounded class final --- core/src/main/java/fj/Bounded.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/fj/Bounded.java b/core/src/main/java/fj/Bounded.java index 980ba9a3..e100b6d6 100644 --- a/core/src/main/java/fj/Bounded.java +++ b/core/src/main/java/fj/Bounded.java @@ -4,7 +4,7 @@ * The Bounded class is used to name the upper and lower limits of a type. * Ord is not a superclass of Bounded since types that are not totally ordered may also have upper and lower bounds. */ -public class Bounded { +public final class Bounded { private final Definition def; From dc9e0a9de8b897cd3ff53bf5c154494786b9dd98 Mon Sep 17 00:00:00 2001 From: Yaroslav Atroshenko Date: Sun, 3 Nov 2019 16:10:18 +0300 Subject: [PATCH 099/173] code clean up --- core/src/test/java/fj/MonoidTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/fj/MonoidTest.java b/core/src/test/java/fj/MonoidTest.java index a54bf672..1156e729 100644 --- a/core/src/test/java/fj/MonoidTest.java +++ b/core/src/test/java/fj/MonoidTest.java @@ -29,7 +29,7 @@ public void intersection_monoid_test() { assertThat(actual, is(Set.set(Ord.intOrd, 3, 4))); } - @Test + @Test public void union_monoid_test() { Monoid> unionMonoid = Monoid.setMonoid(Ord.intOrd); Set first = Set.set(Ord.intOrd, 1, 2, 3, 4); From ac4819d9a71dd82f888db4663f47ab3e39c80cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Fri, 13 Dec 2019 09:11:52 -0500 Subject: [PATCH 100/173] Switch to gradle-versions-plugin --- build.gradle | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 69c01425..5fbad7bc 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,9 @@ defaultTasks 'build' -ext { -} +apply plugin: "com.github.ben-manes.versions" buildscript { - ext { - uptodateVersion = "1.6.3" - } - repositories { mavenLocal() jcenter() @@ -16,7 +11,7 @@ buildscript { } dependencies { - classpath "com.ofg:uptodate-gradle-plugin:$uptodateVersion" + classpath "com.github.ben-manes:gradle-versions-plugin:0.27.0" } wrapper { @@ -98,7 +93,6 @@ subprojects { apply from: "$rootDir/lib.gradle" apply plugin: "java" apply plugin: "eclipse" - apply plugin: "com.ofg.uptodate" repositories { mavenLocal() From 560db2e6d9deaf2bb24bea21d65ab7c410977bce Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Tue, 19 May 2020 20:29:26 -0400 Subject: [PATCH 101/173] Add fj.test.Gen.streamOf(Gen), similar to fj.test.Gen.listOf(Gen, int); returns a generator of streams whose values come from the given generator. --- quickcheck/src/main/java/fj/test/Gen.java | 13 +++++++++++++ quickcheck/src/test/java/fj/test/GenTest.java | 16 ++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/quickcheck/src/main/java/fj/test/Gen.java b/quickcheck/src/main/java/fj/test/Gen.java index c375874d..527d620c 100644 --- a/quickcheck/src/main/java/fj/test/Gen.java +++ b/quickcheck/src/main/java/fj/test/Gen.java @@ -9,6 +9,7 @@ import fj.data.Array; import fj.data.List; import fj.data.Option; +import fj.data.Stream; import fj.function.Effect1; import static fj.Bottom.error; @@ -497,6 +498,18 @@ public static Gen> listOf1(final Gen g) { return listOf(g, 1); } + /** + * Returns a generator of streams whose values come from the given generator. + * + * @param g the generator to produce values from for the returned generator + * @param the type of the generator + * + * @return A generator of streams whose values come from the given generator. + */ + public static Gen> streamOf(final Gen g) { + return gen(i -> r -> Stream.cons(g.gen(i, r), () -> streamOf(g).gen(i, r))); + } + /** * Returns a generator that picks one element from the given list. If the given list is empty, then the * returned generator will never produce a value. diff --git a/quickcheck/src/test/java/fj/test/GenTest.java b/quickcheck/src/test/java/fj/test/GenTest.java index 900369a7..9ec0ec79 100644 --- a/quickcheck/src/test/java/fj/test/GenTest.java +++ b/quickcheck/src/test/java/fj/test/GenTest.java @@ -1,16 +1,19 @@ package fj.test; import fj.data.List; +import fj.data.Stream; import fj.function.Effect1; import org.junit.Test; import static fj.Ord.charOrd; import static fj.data.List.list; import static fj.data.List.range; -import static fj.test.Gen.selectionOf; import static fj.test.Gen.combinationOf; -import static fj.test.Gen.wordOf; import static fj.test.Gen.permutationOf; +import static fj.test.Gen.pickOne; +import static fj.test.Gen.selectionOf; +import static fj.test.Gen.streamOf; +import static fj.test.Gen.wordOf; import static fj.test.Rand.standard; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -175,6 +178,15 @@ public void testWordOf_four() { }); } + @Test + public void testStreamOf() { + final Gen> instance = streamOf(pickOne(AS)); + testPick(100, instance.map(stream -> stream.take(4).toList()), actual -> { + assertEquals(4, actual.length()); + assertTrue(actual.forall(actualA -> AS.exists(a -> a.equals(actualA)))); + }); + } + private static void testPick(int n, Gen> instance, Effect1> test) { range(0, n).map(ignore -> instance.gen(0, standard)).foreachDoEffect(test); } From e98db344df3023beb0898688a682fe73e2c134ec Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Wed, 20 May 2020 19:16:19 -0400 Subject: [PATCH 102/173] Add fj.data.Option.sequence(Validation>). --- core/src/main/java/fj/data/Option.java | 16 +++++++++++--- core/src/test/java/fj/data/OptionTest.java | 25 +++++++++++++++++++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/fj/data/Option.java b/core/src/main/java/fj/data/Option.java index 54b4bb01..1a170c2c 100644 --- a/core/src/main/java/fj/data/Option.java +++ b/core/src/main/java/fj/data/Option.java @@ -372,11 +372,11 @@ public final Option> bindProduct(final Option ob) { public final Option> bindProduct(final Option ob, final Option oc) { return bind(ob, oc, P.p3()); } - + public final Option> bindProduct(final Option ob, final Option oc, final Option od) { return bind(ob, oc, od, P.p4()); } - + public final Option> bindProduct(final Option ob, final Option oc, final Option od, final Option oe) { return bind(ob, oc, od, oe, P.p5()); @@ -712,7 +712,7 @@ public static Option join(final Option> o) { } /** - * Sequence through the option monad. + * Sequence a list through the option monad. * * @param a The list of option to sequence. * @return The option of list after sequencing. @@ -723,6 +723,16 @@ public static Option> sequence(final List> a) { a.head().bind(aa -> sequence(a.tail()).map(cons_(aa))); } + /** + * Sequence a validation through the option monad. + * + * @param a The validation of option to sequence. + * @return The option of validation after sequencing. + */ + public static Option> sequence(final Validation> a) { + return a.traverseOption(identity()); + } + /** * Returns an optional value that has a value of the given argument, if the given predicate holds * on that argument, otherwise, returns no value. diff --git a/core/src/test/java/fj/data/OptionTest.java b/core/src/test/java/fj/data/OptionTest.java index 3eeed489..0966ece0 100644 --- a/core/src/test/java/fj/data/OptionTest.java +++ b/core/src/test/java/fj/data/OptionTest.java @@ -1,9 +1,15 @@ package fj.data; -import org.junit.Assert; import org.junit.Test; +import static fj.data.List.arrayList; +import static fj.data.List.nil; +import static fj.data.Option.none; +import static fj.data.Option.sequence; import static fj.data.Option.some; +import static fj.data.Validation.fail; +import static fj.data.Validation.success; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** @@ -26,4 +32,21 @@ public void traverseList() { assertTrue(actual.equals(expected)); } + @Test + public void sequenceListTest() { + assertEquals(some(nil()), sequence(nil())); + assertEquals(none(), sequence(arrayList(none()))); + assertEquals(some(arrayList(1)), sequence(arrayList(some(1)))); + assertEquals(none(), sequence(arrayList(none(), none()))); + assertEquals(none(), sequence(arrayList(some(1), none()))); + assertEquals(none(), sequence(arrayList(none(), some(2)))); + assertEquals(some(arrayList(1, 2)), sequence(arrayList(some(1), some(2)))); + } + + @Test + public void sequenceValidationTest() { + assertEquals(some(fail(1)), sequence(Validation.>fail(1))); + assertEquals(none(), sequence(Validation.>success(none()))); + assertEquals(some(success("string")), sequence(Validation.>success(some("string")))); + } } From 0a50e8b02c8dd00f7039d40f10eeefc10bfb3547 Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Mon, 20 Jul 2020 21:55:04 -0400 Subject: [PATCH 103/173] Fixes #401. Add fj.test.Gen.sequence(Validation>). --- quickcheck/src/main/java/fj/test/Gen.java | 14 ++++++++++++++ quickcheck/src/test/java/fj/test/GenTest.java | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/quickcheck/src/main/java/fj/test/Gen.java b/quickcheck/src/main/java/fj/test/Gen.java index 527d620c..dabaf9a6 100644 --- a/quickcheck/src/main/java/fj/test/Gen.java +++ b/quickcheck/src/main/java/fj/test/Gen.java @@ -10,6 +10,7 @@ import fj.data.List; import fj.data.Option; import fj.data.Stream; +import fj.data.Validation; import fj.function.Effect1; import static fj.Bottom.error; @@ -319,6 +320,19 @@ public static Gen> sequenceN(final int n, final Gen g) { return sequence(replicate(n, g)); } + /** + * Transform a validation for a generator into a generator of validations: if the given validation is a failure, the + * generator produces that failure value; if the given validation is a success, the generator produces success values. + * + * @param gv The validation for a generator. + * @param the type of the value + * @param the type of the failure + * @return if the given validation is a failure, the generator produces that failure value; if the given validation is a success, the generator produces success values. + */ + public static Gen> sequence(final Validation> gv) { + return gen(i -> r -> gv.map(g -> g.gen(i, r))); + } + /** * Constructs a generator that can access its construction arguments — size and random * generator. diff --git a/quickcheck/src/test/java/fj/test/GenTest.java b/quickcheck/src/test/java/fj/test/GenTest.java index 9ec0ec79..c13480a1 100644 --- a/quickcheck/src/test/java/fj/test/GenTest.java +++ b/quickcheck/src/test/java/fj/test/GenTest.java @@ -1,17 +1,27 @@ package fj.test; +import fj.Equal; import fj.data.List; +import fj.data.NonEmptyList; +import fj.data.Option; import fj.data.Stream; +import fj.data.Validation; import fj.function.Effect1; import org.junit.Test; import static fj.Ord.charOrd; import static fj.data.List.list; import static fj.data.List.range; +import static fj.data.NonEmptyList.nel; +import static fj.data.Option.somes; +import static fj.data.Validation.fail; +import static fj.data.Validation.success; import static fj.test.Gen.combinationOf; +import static fj.test.Gen.listOf; import static fj.test.Gen.permutationOf; import static fj.test.Gen.pickOne; import static fj.test.Gen.selectionOf; +import static fj.test.Gen.sequence; import static fj.test.Gen.streamOf; import static fj.test.Gen.wordOf; import static fj.test.Rand.standard; @@ -187,6 +197,15 @@ public void testStreamOf() { }); } + @Test + public void testSequenceValidation() { + final Gen, Character>>> success = listOf(sequence(success(pickOne(AS))), 4); + testPick(100, success, list -> assertEquals(list.length(),somes(list.map(v -> Option.sequence(v.map(c -> AS.elementIndex(Equal.anyEqual(), c))))).length())); + + final Gen, Gen>>> failure = listOf(sequence(fail(nel(new Exception()))), 4); + testPick(100, failure, list -> assertTrue(list.forall(a -> a.isFail()))); + } + private static void testPick(int n, Gen> instance, Effect1> test) { range(0, n).map(ignore -> instance.gen(0, standard)).foreachDoEffect(test); } From b8c309374065364ae5922244795efbe8e40adffe Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Thu, 23 Jul 2020 13:42:30 -0400 Subject: [PATCH 104/173] Fixes #403. Add fj.data.Validation.sequence*, fj.data.Validation.traverse*. --- core/src/main/java/fj/data/Validation.java | 358 ++++++++++++++++-- .../src/test/java/fj/data/ValidationTest.java | 209 ++++++++++ 2 files changed, 530 insertions(+), 37 deletions(-) create mode 100644 core/src/test/java/fj/data/ValidationTest.java diff --git a/core/src/main/java/fj/data/Validation.java b/core/src/main/java/fj/data/Validation.java index 58aa5e9c..2afb91f3 100644 --- a/core/src/main/java/fj/data/Validation.java +++ b/core/src/main/java/fj/data/Validation.java @@ -1,17 +1,18 @@ package fj.data; import fj.*; +import fj.control.Trampoline; import fj.function.Effect1; -import static fj.Function.curry; -import static fj.P.p; +import java.util.Iterator; -import static fj.Unit.unit; import static fj.Bottom.error; +import static fj.Function.*; +import static fj.P.p; +import static fj.Unit.unit; +import static fj.data.Either.*; import static fj.data.List.list; -import java.util.Iterator; - /** * Isomorphic to {@link Either} but has renamed functions and represents failure on the left and success on the right. * This type also has accumulating functions that accept a {@link Semigroup} for binding computation while keeping error @@ -825,44 +826,327 @@ public static Validation, List> sequenceNonCumulative(List List> traverseList(F> f){ - return isSuccess() ? - f.f(success()).map(Validation::success) : - List.iterableList(fail(e.left().value())); - } + /** + * Sequence the given validation and collect the output on the left side of an either. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static final Either, R> sequenceEitherLeft(final Validation> validation) { + return validation.traverseEitherLeft(identity()); + } - public final Stream> traverseStream(F> f){ - return isSuccess() ? - f.f(success()).map(Validation::success) : - Stream.iterableStream(fail(e.left().value())); - } + /** + * Sequence the given validation and collect the output on the right side of an either. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static final Either> sequenceEitherRight(final Validation> validation) { + return validation.traverseEitherRight(identity()); + } - public final Option> traverseOption(F> f){ - return isSuccess() ? - f.f(success()).map(Validation::success) : - Option.some(fail(e.left().value())); - } + /** + * Sequence the given validation and collect the output as a function. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of input value + * @param the type of output value + * @return the function + */ + public static final F> sequenceF(final Validation> validation) { + return validation.traverseF(identity()); + } - public final IO> traverseIO(F> f){ - return isSuccess() ? - IOFunctions.map(f.f(success()), Validation::success) : - IOFunctions.unit(fail(e.left().value())); - } + /** + * Sequence the given validation and collect the output as an IO. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of the IO value + * @return the IO + */ + public static final IO> sequenceIO(final Validation> validation) { + return validation.traverseIO(identity()); + } - public final P1> traverseP1(F> f){ - return isSuccess() ? - f.f(success()).map(Validation::success) : - p(fail(e.left().value())); - } + /** + * Sequence the given validation and collect the output as a list. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of the list value + * @return the list + */ + public static final List> sequenceList(final Validation> validation) { + return validation.traverseList(identity()); + } + + /** + * Sequence the given validation and collect the output as an option. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of the option value + * @return the option + */ + public static final Option> sequenceOption(final Validation> validation) { + return validation.traverseOption(identity()); + } + /** + * Sequence the given validation and collect the output as a P1. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of the P1 value + * @return the P1 + */ + public static final P1> sequenceP1(final Validation> validation) { + return validation.traverseP1(identity()); + } - public static List fails(List> list) { - return list.filter(Validation::isFail).map(v -> v.fail()); - } + /** + * Sequence the given validation and collect the output as a seq. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of the seq value + * @return the seq + */ + public static final Seq> sequenceSeq(final Validation> validation) { + return validation.traverseSeq(identity()); + } - public static List successes(List> list) { - return list.filter(Validation::isSuccess).map(v -> v.success()); - } + /** + * Sequence the given validation and collect the output as a set. + * + * @param ordE the given failure value ord + * @param ordC the given success value ord + * @param validation the given validation + * @param the type of the failure value + * @param the type of the set value + * @return the set + */ + public static final Set> sequenceSet(final Ord ordE, final Ord ordC, final Validation> validation) { + return validation.traverseSet(ordE, ordC, identity()); + } + + /** + * Sequence the given validation and collect the output as a stream. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of the stream value + * @return the stream + */ + public static final Stream> sequenceStream(final Validation> validation) { + return validation.traverseStream(identity()); + } + + /** + * Sequence the given validation and collect the output as a trampoline. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of the trampoline value + * @return the trampoline + */ + public static final Trampoline> sequenceTrampoline(final Validation> validation) { + return validation.traverseTrampoline(identity()); + } + + /** + * Sequence the given validation and collect the output as a validation. + * + * @param validation the given validation + * @param the type of the failure value + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public static final Validation> sequenceValidation(final Validation> validation) { + return validation.traverseValidation(identity()); + } + + /** + * Traverse this validation with the given function and collect the output on the left side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the list + */ + public final Either, R> traverseEitherLeft(final F> f) { + return validation( + failure -> left(fail(failure)), + success -> f.f(success).left().map(Validation::success)); + } + + /** + * Traverse this validation with the given function and collect the output on the right side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the list + */ + public final Either> traverseEitherRight(final F> f) { + return validation( + failure -> right(fail(failure)), + success -> f.f(success).right().map(Validation::success)); + } + + /** + * Traverse this validation with the given function and collect the output as a function. + * + * @param f the given function + * @param the type of the input value + * @param the type of the output value + * @return the function + */ + public final F> traverseF(final F> f) { + return validation( + failure -> constant(fail(failure)), + success -> andThen(f.f(success), Validation::success)); + } + + /** + * Traverse this validation with the given function and collect the output as an IO. + * + * @param f the given function + * @param the type of the IO value + * @return the IO + */ + public final IO> traverseIO(final F> f) { + return validation( + failure -> IOFunctions.unit(fail(failure)), + success -> IOFunctions.map(f.f(success), Validation::success)); + } + + /** + * Traverse this validation with the given function and collect the output as a list. + * + * @param f the given function + * @param the type of the list value + * @return the list + */ + public final List> traverseList(final F> f) { + return validation( + failure -> List.single(fail(failure)), + success -> f.f(success).map(Validation::success)); + } + + /** + * Traverse this validation with the given function and collect the output as an option. + * + * @param f the given function + * @param the type of the option value + * @return the option + */ + public final Option> traverseOption(F> f) { + return validation( + failure -> Option.some(fail(failure)), + success -> f.f(success).map(Validation::success)); + } + + /** + * Traverse this validation with the given function and collect the output as a P1. + * + * @param f the given function + * @param the type of the P1 value + * @return the P1 + */ + public final P1> traverseP1(final F> f) { + return validation( + failure -> p(fail(failure)), + success -> f.f(success).map(Validation::success)); + } + + /** + * Traverse this validation with the given function and collect the output as a seq. + * + * @param f the given function + * @param the type of the seq value + * @return the seq + */ + public final Seq> traverseSeq(final F> f) { + return validation( + failure -> Seq.single(fail(failure)), + success -> f.f(success).map(Validation::success)); + } + + /** + * Traverse this validation with the given function and collect the output as a set; use the given success and failure value ords to order the set. + * + * @param ordE the given failure value ord + * @param ordC the given success value ord + * @param f the given function + * @param the type of the set value + * @return the set + */ + public final Set> traverseSet(final Ord ordE, final Ord ordC, final F> f) { + final Ord> ord = Ord.validationOrd(ordE, ordC); + return validation( + failure -> Set.single(ord, fail(failure)), + success -> f.f(success).map(ord, Validation::success)); + } + + /** + * Traverse this validation with the given function and collect the output as a stream. + * + * @param f the given function + * @param the type of the stream value + * @return the stream + */ + public final Stream> traverseStream(final F> f) { + return validation( + failure -> Stream.single(fail(failure)), + success -> f.f(success).map(Validation::success)); + } + + /** + * Traverse this validation with the given function and collect the output as a trampoline. + * + * @param f the given function + * @param the type of the trampoline value + * @return the trampoline + */ + public final Trampoline> traverseTrampoline(final F> f) { + return validation( + failure -> Trampoline.pure(fail(failure)), + success -> f.f(success).map(Validation::success)); + } + + /** + * Traverse this validation with the given function and collect the output as a validation. + * + * @param f the given function + * @param the type of the failure value + * @param the type of the seq value + * @return the validation + */ + public final Validation> traverseValidation(final F> f) { + return validation( + failure -> success(fail(failure)), + success -> f.f(success).map(Validation::success)); + } + + + public static List fails(List> list) { + return list.filter(Validation::isFail).map(v -> v.fail()); + } + + public static List successes(List> list) { + return list.filter(Validation::isSuccess).map(v -> v.success()); + } /** * A failing projection of a validation. @@ -1268,7 +1552,7 @@ public static Validation parseShort(final String s } /** - * A function that parses a string into a short. + * A function that parses a string into a short. */ public static final F> parseShort = Validation::parseShort; diff --git a/core/src/test/java/fj/data/ValidationTest.java b/core/src/test/java/fj/data/ValidationTest.java new file mode 100644 index 00000000..0f1f3216 --- /dev/null +++ b/core/src/test/java/fj/data/ValidationTest.java @@ -0,0 +1,209 @@ +package fj.data; + +import fj.control.Trampoline; +import org.junit.Test; + +import java.io.IOException; + +import static fj.Function.constant; +import static fj.Ord.*; +import static fj.P.p; +import static fj.data.Either.*; +import static fj.data.Option.*; +import static fj.data.Validation.sequenceEitherLeft; +import static fj.data.Validation.sequenceEitherRight; +import static fj.data.Validation.sequenceF; +import static fj.data.Validation.sequenceIO; +import static fj.data.Validation.sequenceList; +import static fj.data.Validation.sequenceOption; +import static fj.data.Validation.sequenceP1; +import static fj.data.Validation.sequenceSeq; +import static fj.data.Validation.sequenceSet; +import static fj.data.Validation.sequenceStream; +import static fj.data.Validation.sequenceTrampoline; +import static fj.data.Validation.sequenceValidation; +import static fj.data.Validation.*; +import static org.junit.Assert.assertEquals; + +public class ValidationTest { + + @Test + public void testSequenceEitherLeft() { + assertEquals(left(fail("zero")), sequenceEitherLeft(fail("zero"))); + assertEquals(left(success("zero")), sequenceEitherLeft(success(left("zero")))); + assertEquals(right("zero"), sequenceEitherLeft(success(right("zero")))); + } + + @Test + public void testSequenceEitherRight() { + assertEquals(right(fail("zero")), sequenceEitherRight(fail("zero"))); + assertEquals(right(success("zero")), sequenceEitherRight(success(right("zero")))); + assertEquals(left("zero"), sequenceEitherRight(success(left("zero")))); + } + + @Test + public void testSequenceF() { + assertEquals(constant(fail("zero")).f(1), sequenceF(fail("zero")).f(1)); + assertEquals(constant(success("zero")).f(1), sequenceF(success(constant("zero"))).f(1)); + } + + @Test + public void testSequenceIO() throws IOException { + assertEquals(IOFunctions.lazy(constant(fail("zero"))).run(), sequenceIO(fail("zero")).run()); + assertEquals(IOFunctions.lazy(constant(success("zero"))).run(), sequenceIO(success(IOFunctions.lazy(constant("zero")))).run()); + } + + @Test + public void testSequenceList() { + assertEquals(List.single(fail("zero")), sequenceList(fail("zero"))); + assertEquals(List.nil(), sequenceList(success(List.nil()))); + assertEquals(List.single(success("zero")), sequenceList(success(List.single("zero")))); + assertEquals(List.arrayList(success("zero"), success("one")), sequenceList(success(List.arrayList("zero", "one")))); + } + + @Test + public void testSequenceOption() { + assertEquals(some(fail("zero")), sequenceOption(fail("zero"))); + assertEquals(none(), sequenceOption(success(none()))); + assertEquals(some(success("zero")), sequenceOption(success(some("zero")))); + } + + @Test + public void testSequenceP1() { + assertEquals(p(fail("zero")), sequenceP1(fail("zero"))); + assertEquals(p(success("zero")), sequenceP1(success(p("zero")))); + } + + @Test + public void testSequenceSeq() { + assertEquals(Seq.single(fail("zero")), sequenceSeq(fail("zero"))); + assertEquals(Seq.empty(), sequenceSeq(success(Seq.empty()))); + assertEquals(Seq.single(success("zero")), sequenceSeq(success(Seq.single("zero")))); + assertEquals(Seq.arraySeq(success("zero"), success("one")), sequenceSeq(success(Seq.arraySeq("zero", "one")))); + } + + @Test + public void testSequenceSet() { + assertEquals(Set.single(validationOrd(stringOrd, intOrd), fail("zero")), sequenceSet(stringOrd, intOrd, fail("zero"))); + assertEquals(Set.empty(validationOrd(stringOrd, intOrd)), sequenceSet(stringOrd, intOrd, success(Set.empty(intOrd)))); + assertEquals(Set.single(validationOrd(intOrd, stringOrd), success("zero")), sequenceSet(intOrd, stringOrd, success(Set.single(stringOrd, "zero")))); + assertEquals(Set.arraySet(validationOrd(intOrd, stringOrd), success("zero"), success("one")), sequenceSet(intOrd, stringOrd, Validation.success(Set.arraySet(stringOrd, "zero", "one")))); + } + + @Test + public void testSequenceStream() { + assertEquals(Stream.single(fail("zero")), sequenceStream(fail("zero"))); + assertEquals(Stream.nil(), sequenceStream(success(Stream.nil()))); + assertEquals(Stream.single(success("zero")), sequenceStream(success(Stream.single("zero")))); + assertEquals(Stream.arrayStream(success("zero"), success("one")), sequenceStream(success(Stream.arrayStream("zero", "one")))); + } + + @Test + public void testSequenceTrampoline() { + assertEquals(Trampoline.pure(fail("zero")).run(), sequenceTrampoline(fail("zero")).run()); + assertEquals(Trampoline.pure(success(0)).run(), sequenceTrampoline(success(Trampoline.pure(0))).run()); + } + + @Test + public void testSequenceValidation() { + assertEquals(success(fail("zero")), sequenceValidation(fail("zero"))); + assertEquals(fail("zero"), sequenceValidation(success(fail("zero")))); + assertEquals(success(success(0)), sequenceValidation(success(success(0)))); + } + + @Test + public void testTraverseEitherLeft() { + assertEquals(left(fail("zero")), fail("zero").traverseEitherLeft(constant(left(0)))); + assertEquals(left(success(0)), success("zero").traverseEitherLeft(constant(left(0)))); + assertEquals(left(fail("zero")), fail("zero").traverseEitherLeft(constant(right(0)))); + assertEquals(right(0), success("zero").traverseEitherLeft(constant(right(0)))); + } + + @Test + public void testTraverseEitherRight() { + assertEquals(right(fail("zero")), fail("zero").traverseEitherRight(constant(right(0)))); + assertEquals(right(success(0)), success("zero").traverseEitherRight(constant(right(0)))); + assertEquals(right(fail("zero")), fail("zero").traverseEitherRight(constant(left(0)))); + assertEquals(left(0), success("zero").traverseEitherRight(constant(left(0)))); + } + + @Test + public void testTraverseF() { + assertEquals(constant(fail("zero")).f(1), fail("zero").traverseF(constant(constant(0))).f(1)); + assertEquals(constant(success(0)).f(1), success("zero").traverseF(constant(constant(0))).f(1)); + } + + @Test + public void testTraverseIO() throws IOException { + assertEquals(IOFunctions.lazy(constant(fail("zero"))).run(), fail("zero").traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + assertEquals(IOFunctions.lazy(constant(success(0))).run(), success("zero").traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + } + + @Test + public void testTraverseList() { + assertEquals(List.single(fail("zero")), fail("zero").traverseList(constant(List.nil()))); + assertEquals(List.nil(), success("zero").traverseList(constant(List.nil()))); + assertEquals(List.single(fail("zero")), fail("zero").traverseList(constant(List.single(0)))); + assertEquals(List.single(success(0)), success("zero").traverseList(constant(List.single(0)))); + assertEquals(List.single(fail("zero")), fail("zero").traverseList(constant(List.arrayList(0, 1)))); + assertEquals(List.arrayList(success(0), success(1)), success("zero").traverseList(constant(List.arrayList(0, 1)))); + } + + @Test + public void testTraverseOption() { + assertEquals(some(fail("zero")), fail("zero").traverseOption(constant(none()))); + assertEquals(none(), success("zero").traverseOption(constant(none()))); + assertEquals(some(fail("zero")), fail("zero").traverseOption(constant(some(0)))); + assertEquals(some(success(0)), success("zero").traverseOption(constant(some(0)))); + } + + @Test + public void testTraverseP1() { + assertEquals(p(fail("zero")), fail("zero").traverseP1(constant(p(0)))); + assertEquals(p(success(0)), success("zero").traverseP1(constant(p(0)))); + } + + @Test + public void testTraverseSeq() { + assertEquals(Seq.single(fail("zero")), fail("zero").traverseSeq(constant(Seq.empty()))); + assertEquals(Seq.empty(), success("zero").traverseSeq(constant(Seq.empty()))); + assertEquals(Seq.single(fail("zero")), fail("zero").traverseSeq(constant(Seq.single(0)))); + assertEquals(Seq.single(success(0)), success("zero").traverseSeq(constant(Seq.single(0)))); + assertEquals(Seq.single(fail("zero")), fail("zero").traverseSeq(constant(Seq.arraySeq(0, 1)))); + assertEquals(Seq.arraySeq(success(0), success(1)), success("zero").traverseSeq(constant(Seq.arraySeq(0, 1)))); + } + + @Test + public void testTraverseSet() { + assertEquals(Set.single(validationOrd(stringOrd, intOrd), fail("zero")), fail("zero").traverseSet(stringOrd, intOrd, constant(Set.empty(intOrd)))); + assertEquals(Set.empty(validationOrd(stringOrd, intOrd)), Validation.success("zero").traverseSet(stringOrd, intOrd, constant(Set.empty(intOrd)))); + assertEquals(Set.single(validationOrd(stringOrd, intOrd), fail("zero")), fail("zero").traverseSet(stringOrd, intOrd, constant(Set.single(intOrd, 0)))); + assertEquals(Set.single(validationOrd(stringOrd, intOrd), success(0)), Validation.success("zero").traverseSet(stringOrd, intOrd, constant(Set.single(intOrd, 0)))); + assertEquals(Set.single(validationOrd(stringOrd, intOrd), fail("zero")), fail("zero").traverseSet(stringOrd, intOrd, constant(Set.arraySet(intOrd, 0, 1)))); + assertEquals(Set.arraySet(validationOrd(stringOrd, intOrd), success(0), success(1)), Validation.success("zero").traverseSet(stringOrd, intOrd, constant(Set.arraySet(intOrd, 0, 1)))); + } + + @Test + public void testTraverseStream() { + assertEquals(Stream.single(fail("zero")), fail("zero").traverseStream(constant(Stream.nil()))); + assertEquals(Stream.nil(), success("zero").traverseStream(constant(Stream.nil()))); + assertEquals(Stream.single(fail("zero")), fail("zero").traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.single(success(0)), success("zero").traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.single(fail("zero")), fail("zero").traverseStream(constant(Stream.arrayStream(0, 1)))); + assertEquals(Stream.arrayStream(success(0), success(1)), success("zero").traverseStream(constant(Stream.arrayStream(0, 1)))); + } + + @Test + public void testTraverseTrampoline() { + assertEquals(Trampoline.pure(fail("zero")).run(), fail("zero").traverseTrampoline(constant(Trampoline.pure(0))).run()); + assertEquals(Trampoline.pure(success(0)).run(), success("zero").traverseTrampoline(constant(Trampoline.pure(0))).run()); + } + + @Test + public void testTraverseValidation() { + assertEquals(Validation.>success(fail("zero")), Validation.fail("zero").traverseValidation(constant(Validation.fail(0)))); + assertEquals(Validation.>fail(0), Validation.success("zero").traverseValidation(constant(Validation.fail(0)))); + assertEquals(Validation.>success(fail("zero")), Validation.fail("zero").traverseValidation(constant(Validation.success(0)))); + assertEquals(Validation.>success(success(0)), Validation.success("zero").traverseValidation(constant(Validation.success(0)))); + } +} From cb99e095a2fbc2bcf9f527dbe1d272515c715b0c Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Thu, 23 Jul 2020 12:04:17 -0400 Subject: [PATCH 105/173] Fixes #403. Add fj.data.Option.sequence*, fj.data.Option.traverse*. --- core/src/main/java/fj/data/Option.java | 351 +++++++++++++++++---- core/src/test/java/fj/data/OptionTest.java | 242 ++++++++++++-- 2 files changed, 515 insertions(+), 78 deletions(-) diff --git a/core/src/main/java/fj/data/Option.java b/core/src/main/java/fj/data/Option.java index 1a170c2c..6b62d2cf 100644 --- a/core/src/main/java/fj/data/Option.java +++ b/core/src/main/java/fj/data/Option.java @@ -1,43 +1,24 @@ package fj.data; -import static fj.Bottom.error; -import fj.F; -import fj.F0; -import fj.F2; -import fj.P; -import fj.P1; -import fj.P2; -import fj.P3; -import fj.P4; -import fj.P5; -import fj.P6; -import fj.P7; -import fj.P8; -import fj.Unit; -import fj.Show; +import fj.*; +import fj.control.Trampoline; +import fj.data.optic.*; import fj.function.Effect1; -import fj.Equal; -import fj.Ord; -import fj.Hash; -import fj.data.optic.Prism; -import fj.data.optic.PPrism; + +import java.lang.Class; +import java.util.*; + +import static fj.Bottom.error; import static fj.Function.*; import static fj.P.p; +import static fj.Show.optionShow; import static fj.Unit.unit; -import static fj.data.List.cons; -import static fj.data.List.cons_; -import static fj.data.Validation.parseByte; -import static fj.data.Validation.parseDouble; -import static fj.data.Validation.parseFloat; -import static fj.data.Validation.parseInt; -import static fj.data.Validation.parseLong; -import static fj.data.Validation.parseShort; -import static fj.data.optic.Prism.prism; +import static fj.control.Trampoline.pure; +import static fj.data.Either.*; +import static fj.data.List.*; +import static fj.data.Validation.*; import static fj.data.optic.PPrism.pPrism; -import static fj.Show.optionShow; - -import java.util.Collection; -import java.util.Iterator; +import static fj.data.optic.Prism.prism; /** * An optional value that may be none (no value) or some (a value). This type is a replacement for @@ -411,45 +392,307 @@ public final Option sequence(final Option o) { return bind(c); } - public final Either> traverseEither(F> f) { - return map(a -> f.f(a).right().map(Option::some)).orSome(Either.right(none())); + /** + * Sequence the given option and collect the output on the right side of an either. + * + * @param option the given option + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static final Either, R> sequenceEitherLeft(final Option> option) { + return option.traverseEitherLeft(identity()); + } + + /** + * Sequence the given option and collect the output on the left side of an either. + * + * @param option the given option + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static final Either> sequenceEitherRight(final Option> option) { + return option.traverseEitherRight(identity()); + } + + /** + * Sequence the given option and collect the output as a function. + * + * @param option the given option + * @param the type of the input value + * @param the type of the output value + * @return the either + */ + public static final F> sequenceF(final Option> option) { + return option.traverseF(identity()); + } + + /** + * Sequence the given option and collect the output as an IO. + * + * @param option the given option + * @param the type of the IO value + * @return the IO + */ + public static final IO> sequenceIO(final Option> option) { + return option.traverseIO(identity()); + } + + /** + * Sequence the given option and collect the output as an list. + * + * @param option the given option + * @param the type of the list value + * @return the list + */ + public static final List> sequenceList(final Option> option) { + return option.traverseList(identity()); } - public final IO> traverseIO(F> f) { - return map(a -> IOFunctions.map(f.f(a), Option::some)).orSome(IOFunctions.lazy(Option::none)); + /** + * Sequence the given option and collect the output as an option. + * + * @param option the given option + * @param the type of the option value + * @return the option + */ + public static final Option> sequenceOption(final Option> option) { + return option.traverseOption(identity()); } - public final List> traverseList(F> f) { - return map(a -> f.f(a).map(Option::some)).orSome(List.list()); + /** + * Sequence the given option and collect the output as a P1. + * + * @param option the given option + * @param the type of the P1 value + * @return the P1 + */ + public static final P1> sequenceP1(final Option> option) { + return option.traverseP1(identity()); } - public final Option> traverseOption(F> f) { - return map(f); + /** + * Sequence the given option and collect the output as a seq. + * + * @param option the given option + * @param the type of the seq value + * @return the seq + */ + public static final Seq> sequenceSeq(final Option> option) { + return option.traverseSeq(identity()); } - public final Stream> traverseStream(F> f) { - return map(a -> f.f(a).map(Option::some)).orSome(Stream.nil()); + /** + * Sequence the given option and collect the output as a set; use the given ord to order the set. + * + * @param ord the given ord + * @param option the given option + * @param the type of the set value + * @return the either + */ + public static final Set> sequenceSet(final Ord ord, final Option> option) { + return option.traverseSet(ord, identity()); } - public final P1> traverseP1(F> f) { - return map(a -> f.f(a).map(Option::some)).orSome(p(none())); + /** + * Sequence the given option and collect the output as a stream. + * + * @param option the given option + * @param the type of the stream value + * @return the stream + */ + public static final Stream> sequenceStream(final Option> option) { + return option.traverseStream(identity()); } - public final Seq> traverseSeq(F> f) { - return map(a -> f.f(a).map(Option::some)).orSome(Seq.empty()); + /** + * Sequence the given option and collect the output as a trampoline. + * + * @param option the given trampoline + * @param the type of the stream value + * @return the stream + */ + public static final Trampoline> sequenceTrampoline(final Option> option) { + return option.traverseTrampoline(identity()); } - public final Set> traverseSet(Ord ord, F> f) { - Ord> optOrd = Ord.optionOrd(ord); - return map(a -> f.f(a).map(optOrd, Option::some)).orSome(Set.empty(optOrd)); + /** + * Sequence the given option and collect the output as a validation. + * + * @param option the given option + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public static final Validation> sequenceValidation(final Option> option) { + return option.traverseValidation(identity()); } - public final F2, F>, Set>> traverseSet() { - return this::traverseSet; + /** + * Traverse this option with the given function and collect the output on the left side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public final Either, R> traverseEitherLeft(final F> f) { + return option( + left(none()), + a -> f.f(a).left().map(Option::some)); } - public final Validation> traverseValidation(F> f) { - return map(a -> f.f(a).map(Option::some)).orSome(Validation.success(none())); + /** + * Traverse this option with the given function and collect the output on the right side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public final Either> traverseEitherRight(final F> f) { + return option( + right(none()), + a -> f.f(a).right().map(Option::some)); + } + + /** + * Traverse this option with the given function and collect the output as a function. + * + * @param f the given function + * @param the type of the input value + * @param the type of the output value + * @return the function + */ + public final F> traverseF(final F> f) { + return option( + constant(none()), + a -> andThen(f.f(a), Option::some)); + } + + /** + * Traverse this option with the given function and collect the output as an IO. + * + * @param f the given function + * @param the type of the IO value + * @return the IO + */ + public final IO> traverseIO(final F> f) { + return option( + IOFunctions.lazy(Option::none), + a -> IOFunctions.map(f.f(a), Option::some)); + } + + /** + * Traverse this option with the given function and collect the output as a list. + * + * @param f the given function + * @param the type of the list value + * @return the list + */ + public final List> traverseList(final F> f) { + return option( + List.single(none()), + a -> f.f(a).map(Option::some)); + } + + /** + * Traverse this option with the given function and collect the output as an option. + * + * @param f the given function + * @param the type of the option value + * @return the option + */ + public final Option> traverseOption(final F> f) { + return option( + some(none()), + a -> f.f(a).map(Option::some)); + } + + /** + * Traverse this option with the given function and collect the output as a P1. + * + * @param f the given function + * @param the type of the P1 value + * @return the P1 + */ + public final P1> traverseP1(final F> f) { + return option( + p(none()), + (F>>) a -> f.f(a).map(Option::some)); + } + + /** + * Traverse this option with the given function and collect the output a seq. + * + * @param f the given function + * @param the type of the seq value + * @return the seq + */ + public final Seq> traverseSeq(final F> f) { + return option( + Seq.single(none()), + a -> f.f(a).map(Option::some)); + } + + /** + * Traverse this option with the given function and collect the output as a set; use the given ord to order the set. + * + * @param ord the given ord + * @param f the given function + * @param the type of the set value + * @return the set + */ + public final Set> traverseSet(final Ord ord, final F> f) { + final Ord> ordOption = Ord.optionOrd(ord); + return option( + Set.single(ordOption, none()), + a -> f.f(a).map(ordOption, Option::some)); + } + + /** + * Traverse this option with the given function and collect the output as a stream. + * + * @param f the given function + * @param the type of the stream value + * @return the stream + */ + public final Stream> traverseStream(final F> f) { + return option( + Stream.single(none()), + a -> f.f(a).map(Option::some)); + } + + /** + * Traverse this option with the given function and collect the output as a trampoline. + * + * @param f the given function + * @param the type of the trampoline value + * @return the trampoline + */ + public final Trampoline> traverseTrampoline(final F> f) { + return option( + pure(none()), + a -> f.f(a).map(Option::some)); + } + + /** + * Traverse this option with the given function and collect the output as a validation. + * + * @param f the given function + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public final Validation> traverseValidation(final F> f) { + return option( + success(none()), + a -> f.f(a).map(Option::some)); + } + + public final F2, F>, Set>> traverseSet() { + return this::traverseSet; } /** diff --git a/core/src/test/java/fj/data/OptionTest.java b/core/src/test/java/fj/data/OptionTest.java index 0966ece0..403cbbd1 100644 --- a/core/src/test/java/fj/data/OptionTest.java +++ b/core/src/test/java/fj/data/OptionTest.java @@ -1,21 +1,35 @@ package fj.data; +import fj.control.Trampoline; import org.junit.Test; -import static fj.data.List.arrayList; -import static fj.data.List.nil; -import static fj.data.Option.none; +import java.io.IOException; + +import static fj.Function.constant; +import static fj.Ord.*; +import static fj.P.p; +import static fj.data.Either.*; +import static fj.data.List.*; import static fj.data.Option.sequence; -import static fj.data.Option.some; +import static fj.data.Option.sequenceF; +import static fj.data.Option.sequenceIO; +import static fj.data.Option.sequenceList; +import static fj.data.Option.sequenceOption; +import static fj.data.Option.sequenceP1; +import static fj.data.Option.sequenceSeq; +import static fj.data.Option.sequenceSet; +import static fj.data.Option.sequenceStream; +import static fj.data.Option.sequenceTrampoline; +import static fj.data.Option.sequenceValidation; +import static fj.data.Option.*; import static fj.data.Validation.fail; -import static fj.data.Validation.success; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static fj.data.Validation.*; +import static org.junit.Assert.*; /** * Created by MarkPerry on 15/01/2015. */ -public class OptionTest { +public final class OptionTest { @Test public void equals() { @@ -32,21 +46,201 @@ public void traverseList() { assertTrue(actual.equals(expected)); } - @Test - public void sequenceListTest() { - assertEquals(some(nil()), sequence(nil())); - assertEquals(none(), sequence(arrayList(none()))); - assertEquals(some(arrayList(1)), sequence(arrayList(some(1)))); - assertEquals(none(), sequence(arrayList(none(), none()))); - assertEquals(none(), sequence(arrayList(some(1), none()))); - assertEquals(none(), sequence(arrayList(none(), some(2)))); - assertEquals(some(arrayList(1, 2)), sequence(arrayList(some(1), some(2)))); - } + @Test + public void sequenceListTest() { + assertEquals(some(nil()), sequence(nil())); + assertEquals(none(), sequence(arrayList(none()))); + assertEquals(some(arrayList(1)), sequence(arrayList(some(1)))); + assertEquals(none(), sequence(arrayList(none(), none()))); + assertEquals(none(), sequence(arrayList(some(1), none()))); + assertEquals(none(), sequence(arrayList(none(), some(2)))); + assertEquals(some(arrayList(1, 2)), sequence(arrayList(some(1), some(2)))); + } - @Test - public void sequenceValidationTest() { - assertEquals(some(fail(1)), sequence(Validation.>fail(1))); - assertEquals(none(), sequence(Validation.>success(none()))); - assertEquals(some(success("string")), sequence(Validation.>success(some("string")))); - } + @Test + public void sequenceValidationTest() { + assertEquals(some(fail(1)), sequence(Validation.>fail(1))); + assertEquals(none(), sequence(Validation.>success(none()))); + assertEquals(some(success("string")), sequence(Validation.>success(some("string")))); + } + + @Test + public void testSequenceEitherLeft() { + assertEquals(left(none()), sequenceEitherLeft(none())); + assertEquals(left(some("0")), sequenceEitherLeft(some(left("0")))); + assertEquals(right("0"), sequenceEitherLeft(some(right("0")))); + } + + @Test + public void testSequenceEitherRight() { + assertEquals(right(none()), sequenceEitherRight(none())); + assertEquals(right(some("0")), sequenceEitherRight(some(right("0")))); + assertEquals(left("0"), sequenceEitherRight(some(left("0")))); + } + + @Test + public void testSequenceF() { + assertEquals(constant(none()).f(1), sequenceF(none()).f(1)); + assertEquals(constant(some("0")).f(1), sequenceF(some(constant("0"))).f(1)); + } + + @Test + public void testSequenceIO() throws IOException { + assertEquals(IOFunctions.lazy(constant(none())).run(), sequenceIO(none()).run()); + assertEquals(IOFunctions.lazy(constant(some("0"))).run(), sequenceIO(some(IOFunctions.lazy(constant("0")))).run()); + } + + @Test + public void testSequenceList() { + assertEquals(List.arrayList(none()), sequenceList(none())); + assertEquals(List.nil(), sequenceList(some(List.nil()))); + assertEquals(List.arrayList(some("0")), sequenceList(some(List.single("0")))); + assertEquals(List.arrayList(some("0"), some("1")), sequenceList(some(List.arrayList("0", "1")))); + } + + @Test + public void testSequenceOption() { + assertEquals(some(none()), sequenceOption(none())); + assertEquals(none(), sequenceOption(some(none()))); + assertEquals(some(some("0")), sequenceOption(some(some("0")))); + } + + @Test + public void testSequenceP1() { + assertEquals(p(none()), sequenceP1(none())); + assertEquals(p(some("0")), sequenceP1(some(p("0")))); + } + + @Test + public void testSequenceSeq() { + assertEquals(Seq.arraySeq(none()), sequenceSeq(none())); + assertEquals(Seq.empty(), sequenceSeq(some(Seq.empty()))); + assertEquals(Seq.arraySeq(some("0")), sequenceSeq(some(Seq.single("0")))); + assertEquals(Seq.arraySeq(some("0"), some("1")), sequenceSeq(some(Seq.arraySeq("0", "1")))); + } + + @Test + public void testSequenceSet() { + assertEquals(Set.arraySet(optionOrd(stringOrd), none()), sequenceSet(stringOrd, none())); + assertEquals(Set.empty(optionOrd(stringOrd)), sequenceSet(stringOrd, some(Set.empty(stringOrd)))); + assertEquals(Set.arraySet(optionOrd(stringOrd), some("0")), sequenceSet(stringOrd, some(Set.single(stringOrd, "0")))); + assertEquals(Set.arraySet(optionOrd(stringOrd), some("0"), some("1")), sequenceSet(stringOrd, some(Set.arraySet(stringOrd, "0", "1")))); + } + + @Test + public void testSequenceStream() { + assertEquals(Stream.arrayStream(none()), sequenceStream(none())); + assertEquals(Stream.nil(), sequenceStream(some(Stream.nil()))); + assertEquals(Stream.arrayStream(some("0")), sequenceStream(some(Stream.single("0")))); + assertEquals(Stream.arrayStream(some("0"), some("1")), sequenceStream(some(Stream.arrayStream("0", "1")))); + } + + @Test + public void testSequenceTrampoline() { + assertEquals(Trampoline.pure(none()).run(), sequenceTrampoline(none()).run()); + assertEquals(Trampoline.pure(some(0)).run(), sequenceTrampoline(some(Trampoline.pure(0))).run()); + } + + @Test + public void testSequenceValidation() { + assertEquals(Validation.success(none()), sequenceValidation(none())); + assertEquals(Validation.fail(0), sequenceValidation(some(Validation.fail(0)))); + assertEquals(Validation.success(some(0)), sequenceValidation(some(Validation.success(0)))); + } + + @Test + public void testTraverseEitherLeft() { + assertEquals(left(none()), none().traverseEitherLeft(constant(left(0)))); + assertEquals(left(some(0)), some("0").traverseEitherLeft(constant(left(0)))); + assertEquals(left(none()), none().traverseEitherLeft(constant(right(0)))); + assertEquals(right(0), some("0").traverseEitherLeft(constant(right(0)))); + } + + @Test + public void testTraverseEitherRight() { + assertEquals(right(none()), none().traverseEitherRight(constant(right(0)))); + assertEquals(right(some(0)), some("0").traverseEitherRight(constant(right(0)))); + assertEquals(right(none()), none().traverseEitherRight(constant(left(0)))); + assertEquals(left(0), some("0").traverseEitherRight(constant(left(0)))); + } + + @Test + public void testTraverseF() { + assertEquals(constant(none()).f(1), none().traverseF(constant(constant(0))).f(1)); + assertEquals(constant(some(0)).f(1), some("0").traverseF(constant(constant(0))).f(1)); + } + + @Test + public void testTraverseIO() throws IOException { + assertEquals(IOFunctions.lazy(constant(none())).run(), none().traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + assertEquals(IOFunctions.lazy(constant(some(0))).run(), some("0").traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + } + + @Test + public void testTraverseList() { + assertEquals(List.arrayList(none()), none().traverseList(constant(List.nil()))); + assertEquals(List.nil(), some("0").traverseList(constant(List.nil()))); + assertEquals(List.arrayList(none()), none().traverseList(constant(List.single(0)))); + assertEquals(List.arrayList(some(0)), some("0").traverseList(constant(List.single(0)))); + assertEquals(List.arrayList(none()), none().traverseList(constant(List.arrayList(0, 1)))); + assertEquals(List.arrayList(some(0), some(1)), some("0").traverseList(constant(List.arrayList(0, 1)))); + } + + @Test + public void testTraverseOption() { + assertEquals(some(none()), none().traverseOption(constant(none()))); + assertEquals(none(), some("0").traverseOption(constant(none()))); + assertEquals(some(none()), none().traverseOption(constant(some(0)))); + assertEquals(some(some(0)), some("0").traverseOption(constant(some(0)))); + } + + @Test + public void testTraverseP1() { + assertEquals(p(none()), none().traverseP1(constant(p(0)))); + assertEquals(p(some(0)), some("0").traverseP1(constant(p(0)))); + } + + @Test + public void testTraverseSeq() { + assertEquals(Seq.arraySeq(none()), none().traverseSeq(constant(Seq.empty()))); + assertEquals(Seq.empty(), some("0").traverseSeq(constant(Seq.empty()))); + assertEquals(Seq.arraySeq(none()), none().traverseSeq(constant(Seq.single(0)))); + assertEquals(Seq.arraySeq(some(0)), some("0").traverseSeq(constant(Seq.single(0)))); + assertEquals(Seq.arraySeq(none()), none().traverseSeq(constant(Seq.arraySeq(0, 1)))); + assertEquals(Seq.arraySeq(some(0), some(1)), some("0").traverseSeq(constant(Seq.arraySeq(0, 1)))); + } + + @Test + public void testTraverseSet() { + assertEquals(Set.arraySet(optionOrd(intOrd), none()), none().traverseSet(intOrd, constant(Set.empty(intOrd)))); + assertEquals(Set.empty(optionOrd(intOrd)), some("0").traverseSet(intOrd, constant(Set.empty(intOrd)))); + assertEquals(Set.arraySet(optionOrd(intOrd), none()), none().traverseSet(intOrd, constant(Set.single(intOrd, 0)))); + assertEquals(Set.arraySet(optionOrd(intOrd), some(0)), some("0").traverseSet(intOrd, constant(Set.single(intOrd, 0)))); + assertEquals(Set.arraySet(optionOrd(intOrd), none()), none().traverseSet(intOrd, constant(Set.arraySet(intOrd, 0, 1)))); + assertEquals(Set.arraySet(optionOrd(intOrd), some(0), some(1)), some("0").traverseSet(intOrd, constant(Set.arraySet(intOrd, 0, 1)))); + } + + @Test + public void testTraverseStream() { + assertEquals(Stream.arrayStream(none()), none().traverseStream(constant(Stream.nil()))); + assertEquals(Stream.nil(), some("0").traverseStream(constant(Stream.nil()))); + assertEquals(Stream.arrayStream(none()), none().traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.arrayStream(some(0)), some("0").traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.arrayStream(none()), none().traverseStream(constant(Stream.arrayStream(0, 1)))); + assertEquals(Stream.arrayStream(some(0), some(1)), some("0").traverseStream(constant(Stream.arrayStream(0, 1)))); + } + + @Test + public void testTraverseTrampoline() { + assertEquals(Trampoline.pure(none()).run(), none().traverseTrampoline(constant(Trampoline.pure(0))).run()); + assertEquals(Trampoline.pure(some(0)).run(), some("0").traverseTrampoline(constant(Trampoline.pure(0))).run()); + } + + @Test + public void testTraverseValidation() { + assertEquals(success(none()), none().traverseValidation(constant(Validation.fail(0)))); + assertEquals(fail(0), some("0").traverseValidation(constant(Validation.fail(0)))); + assertEquals(success(none()), none().traverseValidation(constant(Validation.success(0)))); + assertEquals(success(some(0)), some("0").traverseValidation(constant(Validation.success(0)))); + } } From 225622c0e9ac4544868d341455ea5e6894311a10 Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Thu, 23 Jul 2020 13:38:44 -0400 Subject: [PATCH 106/173] Fixes #403. Add fj.data.Option.sequence*, fj.data.Option.traverse*; correct comment, distinguish between 0 and 0. --- core/src/main/java/fj/data/Option.java | 4 +- core/src/test/java/fj/data/OptionTest.java | 106 +++++++++++---------- 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/core/src/main/java/fj/data/Option.java b/core/src/main/java/fj/data/Option.java index 6b62d2cf..655463b7 100644 --- a/core/src/main/java/fj/data/Option.java +++ b/core/src/main/java/fj/data/Option.java @@ -393,7 +393,7 @@ public final Option sequence(final Option o) { } /** - * Sequence the given option and collect the output on the right side of an either. + * Sequence the given option and collect the output on the left side of an either. * * @param option the given option * @param the type of the right value @@ -405,7 +405,7 @@ public static final Either, R> sequenceEitherLeft(final Option< } /** - * Sequence the given option and collect the output on the left side of an either. + * Sequence the given option and collect the output on the right side of an either. * * @param option the given option * @param the type of the right value diff --git a/core/src/test/java/fj/data/OptionTest.java b/core/src/test/java/fj/data/OptionTest.java index 403cbbd1..db37f1ed 100644 --- a/core/src/test/java/fj/data/OptionTest.java +++ b/core/src/test/java/fj/data/OptionTest.java @@ -11,6 +11,8 @@ import static fj.data.Either.*; import static fj.data.List.*; import static fj.data.Option.sequence; +import static fj.data.Option.sequenceEitherLeft; +import static fj.data.Option.sequenceEitherRight; import static fj.data.Option.sequenceF; import static fj.data.Option.sequenceIO; import static fj.data.Option.sequenceList; @@ -67,72 +69,72 @@ public void sequenceValidationTest() { @Test public void testSequenceEitherLeft() { assertEquals(left(none()), sequenceEitherLeft(none())); - assertEquals(left(some("0")), sequenceEitherLeft(some(left("0")))); - assertEquals(right("0"), sequenceEitherLeft(some(right("0")))); + assertEquals(left(some("zero")), sequenceEitherLeft(some(left("zero")))); + assertEquals(right("zero"), sequenceEitherLeft(some(right("zero")))); } @Test public void testSequenceEitherRight() { assertEquals(right(none()), sequenceEitherRight(none())); - assertEquals(right(some("0")), sequenceEitherRight(some(right("0")))); - assertEquals(left("0"), sequenceEitherRight(some(left("0")))); + assertEquals(right(some("zero")), sequenceEitherRight(some(right("zero")))); + assertEquals(left("zero"), sequenceEitherRight(some(left("zero")))); } @Test public void testSequenceF() { assertEquals(constant(none()).f(1), sequenceF(none()).f(1)); - assertEquals(constant(some("0")).f(1), sequenceF(some(constant("0"))).f(1)); + assertEquals(constant(some("zero")).f(1), sequenceF(some(constant("zero"))).f(1)); } @Test public void testSequenceIO() throws IOException { assertEquals(IOFunctions.lazy(constant(none())).run(), sequenceIO(none()).run()); - assertEquals(IOFunctions.lazy(constant(some("0"))).run(), sequenceIO(some(IOFunctions.lazy(constant("0")))).run()); + assertEquals(IOFunctions.lazy(constant(some("zero"))).run(), sequenceIO(some(IOFunctions.lazy(constant("zero")))).run()); } @Test public void testSequenceList() { - assertEquals(List.arrayList(none()), sequenceList(none())); + assertEquals(List.single(none()), sequenceList(none())); assertEquals(List.nil(), sequenceList(some(List.nil()))); - assertEquals(List.arrayList(some("0")), sequenceList(some(List.single("0")))); - assertEquals(List.arrayList(some("0"), some("1")), sequenceList(some(List.arrayList("0", "1")))); + assertEquals(List.single(some("zero")), sequenceList(some(List.single("zero")))); + assertEquals(List.arrayList(some("zero"), some("one")), sequenceList(some(List.arrayList("zero", "one")))); } @Test public void testSequenceOption() { assertEquals(some(none()), sequenceOption(none())); assertEquals(none(), sequenceOption(some(none()))); - assertEquals(some(some("0")), sequenceOption(some(some("0")))); + assertEquals(some(some("zero")), sequenceOption(some(some("zero")))); } @Test public void testSequenceP1() { assertEquals(p(none()), sequenceP1(none())); - assertEquals(p(some("0")), sequenceP1(some(p("0")))); + assertEquals(p(some("zero")), sequenceP1(some(p("zero")))); } @Test public void testSequenceSeq() { - assertEquals(Seq.arraySeq(none()), sequenceSeq(none())); + assertEquals(Seq.single(none()), sequenceSeq(none())); assertEquals(Seq.empty(), sequenceSeq(some(Seq.empty()))); - assertEquals(Seq.arraySeq(some("0")), sequenceSeq(some(Seq.single("0")))); - assertEquals(Seq.arraySeq(some("0"), some("1")), sequenceSeq(some(Seq.arraySeq("0", "1")))); + assertEquals(Seq.single(some("zero")), sequenceSeq(some(Seq.single("zero")))); + assertEquals(Seq.arraySeq(some("zero"), some("one")), sequenceSeq(some(Seq.arraySeq("zero", "one")))); } @Test public void testSequenceSet() { assertEquals(Set.arraySet(optionOrd(stringOrd), none()), sequenceSet(stringOrd, none())); assertEquals(Set.empty(optionOrd(stringOrd)), sequenceSet(stringOrd, some(Set.empty(stringOrd)))); - assertEquals(Set.arraySet(optionOrd(stringOrd), some("0")), sequenceSet(stringOrd, some(Set.single(stringOrd, "0")))); - assertEquals(Set.arraySet(optionOrd(stringOrd), some("0"), some("1")), sequenceSet(stringOrd, some(Set.arraySet(stringOrd, "0", "1")))); + assertEquals(Set.arraySet(optionOrd(stringOrd), some("zero")), sequenceSet(stringOrd, some(Set.single(stringOrd, "zero")))); + assertEquals(Set.arraySet(optionOrd(stringOrd), some("zero"), some("one")), sequenceSet(stringOrd, some(Set.arraySet(stringOrd, "zero", "one")))); } @Test public void testSequenceStream() { - assertEquals(Stream.arrayStream(none()), sequenceStream(none())); + assertEquals(Stream.single(none()), sequenceStream(none())); assertEquals(Stream.nil(), sequenceStream(some(Stream.nil()))); - assertEquals(Stream.arrayStream(some("0")), sequenceStream(some(Stream.single("0")))); - assertEquals(Stream.arrayStream(some("0"), some("1")), sequenceStream(some(Stream.arrayStream("0", "1")))); + assertEquals(Stream.single(some("zero")), sequenceStream(some(Stream.single("zero")))); + assertEquals(Stream.arrayStream(some("zero"), some("one")), sequenceStream(some(Stream.arrayStream("zero", "one")))); } @Test @@ -151,96 +153,96 @@ public void testSequenceValidation() { @Test public void testTraverseEitherLeft() { assertEquals(left(none()), none().traverseEitherLeft(constant(left(0)))); - assertEquals(left(some(0)), some("0").traverseEitherLeft(constant(left(0)))); + assertEquals(left(some(0)), some("zero").traverseEitherLeft(constant(left(0)))); assertEquals(left(none()), none().traverseEitherLeft(constant(right(0)))); - assertEquals(right(0), some("0").traverseEitherLeft(constant(right(0)))); + assertEquals(right(0), some("zero").traverseEitherLeft(constant(right(0)))); } @Test public void testTraverseEitherRight() { assertEquals(right(none()), none().traverseEitherRight(constant(right(0)))); - assertEquals(right(some(0)), some("0").traverseEitherRight(constant(right(0)))); + assertEquals(right(some(0)), some("zero").traverseEitherRight(constant(right(0)))); assertEquals(right(none()), none().traverseEitherRight(constant(left(0)))); - assertEquals(left(0), some("0").traverseEitherRight(constant(left(0)))); + assertEquals(left(0), some("zero").traverseEitherRight(constant(left(0)))); } @Test public void testTraverseF() { assertEquals(constant(none()).f(1), none().traverseF(constant(constant(0))).f(1)); - assertEquals(constant(some(0)).f(1), some("0").traverseF(constant(constant(0))).f(1)); + assertEquals(constant(some(0)).f(1), some("zero").traverseF(constant(constant(0))).f(1)); } @Test public void testTraverseIO() throws IOException { assertEquals(IOFunctions.lazy(constant(none())).run(), none().traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); - assertEquals(IOFunctions.lazy(constant(some(0))).run(), some("0").traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + assertEquals(IOFunctions.lazy(constant(some(0))).run(), some("zero").traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); } @Test public void testTraverseList() { - assertEquals(List.arrayList(none()), none().traverseList(constant(List.nil()))); - assertEquals(List.nil(), some("0").traverseList(constant(List.nil()))); - assertEquals(List.arrayList(none()), none().traverseList(constant(List.single(0)))); - assertEquals(List.arrayList(some(0)), some("0").traverseList(constant(List.single(0)))); - assertEquals(List.arrayList(none()), none().traverseList(constant(List.arrayList(0, 1)))); - assertEquals(List.arrayList(some(0), some(1)), some("0").traverseList(constant(List.arrayList(0, 1)))); + assertEquals(List.single(none()), none().traverseList(constant(List.nil()))); + assertEquals(List.nil(), some("zero").traverseList(constant(List.nil()))); + assertEquals(List.single(none()), none().traverseList(constant(List.single(0)))); + assertEquals(List.single(some(0)), some("zero").traverseList(constant(List.single(0)))); + assertEquals(List.single(none()), none().traverseList(constant(List.arrayList(0, 1)))); + assertEquals(List.arrayList(some(0), some(1)), some("zero").traverseList(constant(List.arrayList(0, 1)))); } @Test public void testTraverseOption() { assertEquals(some(none()), none().traverseOption(constant(none()))); - assertEquals(none(), some("0").traverseOption(constant(none()))); + assertEquals(none(), some("zero").traverseOption(constant(none()))); assertEquals(some(none()), none().traverseOption(constant(some(0)))); - assertEquals(some(some(0)), some("0").traverseOption(constant(some(0)))); + assertEquals(some(some(0)), some("zero").traverseOption(constant(some(0)))); } @Test public void testTraverseP1() { assertEquals(p(none()), none().traverseP1(constant(p(0)))); - assertEquals(p(some(0)), some("0").traverseP1(constant(p(0)))); + assertEquals(p(some(0)), some("zero").traverseP1(constant(p(0)))); } @Test public void testTraverseSeq() { - assertEquals(Seq.arraySeq(none()), none().traverseSeq(constant(Seq.empty()))); - assertEquals(Seq.empty(), some("0").traverseSeq(constant(Seq.empty()))); - assertEquals(Seq.arraySeq(none()), none().traverseSeq(constant(Seq.single(0)))); - assertEquals(Seq.arraySeq(some(0)), some("0").traverseSeq(constant(Seq.single(0)))); - assertEquals(Seq.arraySeq(none()), none().traverseSeq(constant(Seq.arraySeq(0, 1)))); - assertEquals(Seq.arraySeq(some(0), some(1)), some("0").traverseSeq(constant(Seq.arraySeq(0, 1)))); + assertEquals(Seq.single(none()), none().traverseSeq(constant(Seq.empty()))); + assertEquals(Seq.empty(), some("zero").traverseSeq(constant(Seq.empty()))); + assertEquals(Seq.single(none()), none().traverseSeq(constant(Seq.single(0)))); + assertEquals(Seq.single(some(0)), some("zero").traverseSeq(constant(Seq.single(0)))); + assertEquals(Seq.single(none()), none().traverseSeq(constant(Seq.arraySeq(0, 1)))); + assertEquals(Seq.arraySeq(some(0), some(1)), some("zero").traverseSeq(constant(Seq.arraySeq(0, 1)))); } @Test public void testTraverseSet() { assertEquals(Set.arraySet(optionOrd(intOrd), none()), none().traverseSet(intOrd, constant(Set.empty(intOrd)))); - assertEquals(Set.empty(optionOrd(intOrd)), some("0").traverseSet(intOrd, constant(Set.empty(intOrd)))); + assertEquals(Set.empty(optionOrd(intOrd)), some("zero").traverseSet(intOrd, constant(Set.empty(intOrd)))); assertEquals(Set.arraySet(optionOrd(intOrd), none()), none().traverseSet(intOrd, constant(Set.single(intOrd, 0)))); - assertEquals(Set.arraySet(optionOrd(intOrd), some(0)), some("0").traverseSet(intOrd, constant(Set.single(intOrd, 0)))); + assertEquals(Set.arraySet(optionOrd(intOrd), some(0)), some("zero").traverseSet(intOrd, constant(Set.single(intOrd, 0)))); assertEquals(Set.arraySet(optionOrd(intOrd), none()), none().traverseSet(intOrd, constant(Set.arraySet(intOrd, 0, 1)))); - assertEquals(Set.arraySet(optionOrd(intOrd), some(0), some(1)), some("0").traverseSet(intOrd, constant(Set.arraySet(intOrd, 0, 1)))); + assertEquals(Set.arraySet(optionOrd(intOrd), some(0), some(1)), some("zero").traverseSet(intOrd, constant(Set.arraySet(intOrd, 0, 1)))); } @Test public void testTraverseStream() { - assertEquals(Stream.arrayStream(none()), none().traverseStream(constant(Stream.nil()))); - assertEquals(Stream.nil(), some("0").traverseStream(constant(Stream.nil()))); - assertEquals(Stream.arrayStream(none()), none().traverseStream(constant(Stream.single(0)))); - assertEquals(Stream.arrayStream(some(0)), some("0").traverseStream(constant(Stream.single(0)))); - assertEquals(Stream.arrayStream(none()), none().traverseStream(constant(Stream.arrayStream(0, 1)))); - assertEquals(Stream.arrayStream(some(0), some(1)), some("0").traverseStream(constant(Stream.arrayStream(0, 1)))); + assertEquals(Stream.single(none()), none().traverseStream(constant(Stream.nil()))); + assertEquals(Stream.nil(), some("zero").traverseStream(constant(Stream.nil()))); + assertEquals(Stream.single(none()), none().traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.single(some(0)), some("zero").traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.single(none()), none().traverseStream(constant(Stream.arrayStream(0, 1)))); + assertEquals(Stream.arrayStream(some(0), some(1)), some("zero").traverseStream(constant(Stream.arrayStream(0, 1)))); } @Test public void testTraverseTrampoline() { assertEquals(Trampoline.pure(none()).run(), none().traverseTrampoline(constant(Trampoline.pure(0))).run()); - assertEquals(Trampoline.pure(some(0)).run(), some("0").traverseTrampoline(constant(Trampoline.pure(0))).run()); + assertEquals(Trampoline.pure(some(0)).run(), some("zero").traverseTrampoline(constant(Trampoline.pure(0))).run()); } @Test public void testTraverseValidation() { assertEquals(success(none()), none().traverseValidation(constant(Validation.fail(0)))); - assertEquals(fail(0), some("0").traverseValidation(constant(Validation.fail(0)))); + assertEquals(fail(0), some("zero").traverseValidation(constant(Validation.fail(0)))); assertEquals(success(none()), none().traverseValidation(constant(Validation.success(0)))); - assertEquals(success(some(0)), some("0").traverseValidation(constant(Validation.success(0)))); + assertEquals(success(some(0)), some("zero").traverseValidation(constant(Validation.success(0)))); } } From f9ec6ee20804e1db8b6a4c0c238a5a51adbbe888 Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Thu, 23 Jul 2020 17:56:43 -0400 Subject: [PATCH 107/173] Fixes #403. Add fj.data.Option.sequence*, fj.data.Option.traverse*; add bind tests. --- core/src/test/java/fj/data/OptionTest.java | 187 +++++++++++++++++++-- 1 file changed, 174 insertions(+), 13 deletions(-) diff --git a/core/src/test/java/fj/data/OptionTest.java b/core/src/test/java/fj/data/OptionTest.java index db37f1ed..d028d4f6 100644 --- a/core/src/test/java/fj/data/OptionTest.java +++ b/core/src/test/java/fj/data/OptionTest.java @@ -1,5 +1,6 @@ package fj.data; +import fj.P; import fj.control.Trampoline; import org.junit.Test; @@ -7,22 +8,11 @@ import static fj.Function.constant; import static fj.Ord.*; -import static fj.P.p; +import static fj.P.*; import static fj.data.Either.*; import static fj.data.List.*; +import static fj.data.Option.iif; import static fj.data.Option.sequence; -import static fj.data.Option.sequenceEitherLeft; -import static fj.data.Option.sequenceEitherRight; -import static fj.data.Option.sequenceF; -import static fj.data.Option.sequenceIO; -import static fj.data.Option.sequenceList; -import static fj.data.Option.sequenceOption; -import static fj.data.Option.sequenceP1; -import static fj.data.Option.sequenceSeq; -import static fj.data.Option.sequenceSet; -import static fj.data.Option.sequenceStream; -import static fj.data.Option.sequenceTrampoline; -import static fj.data.Option.sequenceValidation; import static fj.data.Option.*; import static fj.data.Validation.fail; import static fj.data.Validation.*; @@ -66,6 +56,177 @@ public void sequenceValidationTest() { assertEquals(some(success("string")), sequence(Validation.>success(some("string")))); } + @Test + public void testBind1() { + range(0, 1).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), 0), list.index(0).bind(Option::some)); + }); + + } + + @Test + public void testBind2() { + range(0, 2).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1)), list.index(0).bind(list.index(1), p2())); + }); + } + + @Test + public void testBind3() { + range(0, 3).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2)), list.index(0).bind(list.index(1), list.index(2), p3())); + }); + + } + + @Test + public void testBind4() { + range(0, 4).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3)), list.index(0).bind(list.index(1), list.index(2), list.index(3), p4())); + }); + + } + + @Test + public void testBind5() { + range(0, 5).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4)), list.index(0).bind(list.index(1), list.index(2), list.index(3), list.index(4), p5())); + }); + } + + @Test + public void testBind6() { + range(0, 6).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4, 5)), list.index(0).bind(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), p6())); + }); + } + + @Test + public void testBind7() { + range(0, 7).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4, 5, 6)), list.index(0).bind(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), p7())); + }); + } + + @Test + public void testBind8() { + range(0, 8).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4, 5, 6, 7)), list.index(0).bind(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), list.index(7), P.p8())); + }); + } + + @Test + public void testBindProduct2() { + range(0, 2).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1)), list.index(0).bindProduct(list.index(1))); + }); + } + + @Test + public void testBindProduct3() { + range(0, 3).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2)), list.index(0).bindProduct(list.index(1), list.index(2))); + }); + + } + + @Test + public void testBindProduct4() { + range(0, 4).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3)), list.index(0).bindProduct(list.index(1), list.index(2), list.index(3))); + }); + + } + + @Test + public void testBindProduct5() { + range(0, 5).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4)), list.index(0).bindProduct(list.index(1), list.index(2), list.index(3), list.index(4))); + }); + } + + @Test + public void testBindProduct6() { + range(0, 6).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4, 5)), list.index(0).bindProduct(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5))); + }); + } + + @Test + public void testBindProduct7() { + range(0, 7).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4, 5, 6)), list.index(0).bindProduct(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6))); + }); + } + + @Test + public void testBindProduct8() { + range(0, 8).map(i -> arrayList(Option.none(), some(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) + .forEach(list -> { + assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4, 5, 6, 7)), list.index(0).bindProduct(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), list.index(7))); + }); + } + + @Test public void testSequenceEitherLeft() { assertEquals(left(none()), sequenceEitherLeft(none())); From fc85ee1ede0d15b64d12f4d0ec12d5ecc79ed0f8 Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Fri, 24 Jul 2020 16:27:38 -0400 Subject: [PATCH 108/173] Fixes #403. Add fj.data.Option.sequence*, fj.data.Option.traverse.*; add sequenceEither, traverseEither as aliases for sequenceEitherRight, sequenceEitherLeft. --- core/src/main/java/fj/data/Option.java | 24 +++++++++++++++++++ core/src/test/java/fj/data/OptionTest.java | 27 ++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/core/src/main/java/fj/data/Option.java b/core/src/main/java/fj/data/Option.java index 655463b7..b6876c63 100644 --- a/core/src/main/java/fj/data/Option.java +++ b/core/src/main/java/fj/data/Option.java @@ -392,6 +392,18 @@ public final Option sequence(final Option o) { return bind(c); } + /** + * Sequence the given option and collect the output on the right side of an either. + * + * @param option the given option + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static final Either> sequenceEither(final Option> option) { + return option.traverseEitherRight(identity()); + } + /** * Sequence the given option and collect the output on the left side of an either. * @@ -529,6 +541,18 @@ public static final Validation> sequenceValidation(final Opt return option.traverseValidation(identity()); } + /** + * Traverse this option with the given function and collect the output on the right side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public final Either> traverseEither(final F> f) { + return traverseEitherRight(f); + } + /** * Traverse this option with the given function and collect the output on the left side of an either. * diff --git a/core/src/test/java/fj/data/OptionTest.java b/core/src/test/java/fj/data/OptionTest.java index d028d4f6..da5ef065 100644 --- a/core/src/test/java/fj/data/OptionTest.java +++ b/core/src/test/java/fj/data/OptionTest.java @@ -13,6 +13,19 @@ import static fj.data.List.*; import static fj.data.Option.iif; import static fj.data.Option.sequence; +import static fj.data.Option.sequenceEither; +import static fj.data.Option.sequenceEitherLeft; +import static fj.data.Option.sequenceEitherRight; +import static fj.data.Option.sequenceF; +import static fj.data.Option.sequenceIO; +import static fj.data.Option.sequenceList; +import static fj.data.Option.sequenceOption; +import static fj.data.Option.sequenceP1; +import static fj.data.Option.sequenceSeq; +import static fj.data.Option.sequenceSet; +import static fj.data.Option.sequenceStream; +import static fj.data.Option.sequenceTrampoline; +import static fj.data.Option.sequenceValidation; import static fj.data.Option.*; import static fj.data.Validation.fail; import static fj.data.Validation.*; @@ -226,6 +239,12 @@ public void testBindProduct8() { }); } + @Test + public void testSequenceEither() { + assertEquals(right(none()), sequenceEither(none())); + assertEquals(right(some("zero")), sequenceEither(some(right("zero")))); + assertEquals(left("zero"), sequenceEither(some(left("zero")))); + } @Test public void testSequenceEitherLeft() { @@ -311,6 +330,14 @@ public void testSequenceValidation() { assertEquals(Validation.success(some(0)), sequenceValidation(some(Validation.success(0)))); } + @Test + public void testTraverseEither() { + assertEquals(right(none()), none().traverseEither(constant(right(0)))); + assertEquals(right(some(0)), some("zero").traverseEither(constant(right(0)))); + assertEquals(right(none()), none().traverseEither(constant(left(0)))); + assertEquals(left(0), some("zero").traverseEither(constant(left(0)))); + } + @Test public void testTraverseEitherLeft() { assertEquals(left(none()), none().traverseEitherLeft(constant(left(0)))); From bf3fed02f7ae94107155ed93e271e9f621b0be2a Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Fri, 24 Jul 2020 12:26:28 -0400 Subject: [PATCH 109/173] Fixes #406. Several fj.data.Validation.accumulate functions construct unexpected or incorrect failures; add to ValidationTest. --- core/src/main/java/fj/data/Validation.java | 26 +- .../src/test/java/fj/data/ValidationTest.java | 245 +++++++++++++++++- 2 files changed, 242 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/fj/data/Validation.java b/core/src/main/java/fj/data/Validation.java index 2afb91f3..e99d64a1 100644 --- a/core/src/main/java/fj/data/Validation.java +++ b/core/src/main/java/fj/data/Validation.java @@ -738,20 +738,14 @@ public final Validation, B> accumulate(F f) { } - public final Validation, C> accumulate(Validation v2, F2 f) { - List list = List.nil(); - if (isFail()) { - list = list.cons(fail()); - } - if (v2.isFail()) { - list = list.cons(v2.fail()); - } - if (!list.isEmpty()) { - return fail(list); - } else { - return success(f.f(success(), v2.success())); - } + public final Validation, C> accumulate(Validation v2, F2 f) { + List list = fails(list(this, v2)); + if (!list.isEmpty()) { + return fail(list); + } else { + return success(f.f(success(), v2.success())); } + } @@ -784,7 +778,7 @@ public final Validation, D> accumulate(Validation v2, Va public final Validation, G> accumulate(Validation v2, Validation v3, Validation v4, Validation v5, Validation v6, F6 f) { - List list = fails(list(this, v2, v3, v4, v5)); + List list = fails(list(this, v2, v3, v4, v5, v6)); if (!list.isEmpty()) { return fail(list); } else { @@ -793,7 +787,7 @@ public final Validation, D> accumulate(Validation v2, Va } public final Validation, H> accumulate(Validation v2, Validation v3, Validation v4, Validation v5, Validation v6, Validation v7, F7 f) { - List list = fails(list(this, v2, v3, v4, v5)); + List list = fails(list(this, v2, v3, v4, v5, v6, v7)); if (!list.isEmpty()) { return fail(list); } else { @@ -802,7 +796,7 @@ public final Validation, D> accumulate(Validation v2, Va } public final Validation, I> accumulate(Validation v2, Validation v3, Validation v4, Validation v5, Validation v6, Validation v7, Validation v8, F8 f) { - List list = fails(list(this, v2, v3, v4, v5)); + List list = fails(list(this, v2, v3, v4, v5, v6, v7, v8)); if (!list.isEmpty()) { return fail(list); } else { diff --git a/core/src/test/java/fj/data/ValidationTest.java b/core/src/test/java/fj/data/ValidationTest.java index 0f1f3216..0df2deb9 100644 --- a/core/src/test/java/fj/data/ValidationTest.java +++ b/core/src/test/java/fj/data/ValidationTest.java @@ -1,14 +1,16 @@ package fj.data; +import fj.*; import fj.control.Trampoline; import org.junit.Test; import java.io.IOException; -import static fj.Function.constant; +import static fj.Function.*; import static fj.Ord.*; -import static fj.P.p; +import static fj.P.*; import static fj.data.Either.*; +import static fj.data.List.*; import static fj.data.Option.*; import static fj.data.Validation.sequenceEitherLeft; import static fj.data.Validation.sequenceEitherRight; @@ -27,6 +29,223 @@ public class ValidationTest { + @Test + public void testAccumulateSemigroup2() { + range(0, 2).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(), list.index(1))); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1)), list.index(0).accumulate(Semigroup.listSemigroup(), list.index(1), p2())); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1)), list.index(0).accumulate(Semigroup.listSemigroup(), list.index(1), uncurryF2(p2()))); + }); + } + + @Test + public void testAccumulateSemigroup3() { + range(0, 3).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> + accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2))); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), p3())); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), uncurryF3(p3()))); + }); + + } + + @Test + public void testAccumulateSemigroup4() { + range(0, 4).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> + accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3))); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), p4())); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), uncurryF4(p4()))); + }); + + } + + @Test + public void testAccumulateSemigroup5() { + range(0, 5).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> + accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4))); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), p5())); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), uncurryF5(p5()))); + }); + } + + @Test + public void testAccumulateSemigroup6() { + range(0, 6).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> + accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5))); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), p6())); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), uncurryF6(p6()))); + }); + } + + @Test + public void testAccumulateSemigroup7() { + range(0, 7).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> + accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6))); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5, 6)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), p7())); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5, 6)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), uncurryF7(p7()))); + }); + } + + @Test + public void testAccumulateSemigroup8() { + range(0, 8).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> + accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), list.index(7))); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5, 6, 7)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), list.index(7), P.p8())); + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5, 6, 7)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), list.index(7), uncurryF8(P.p8()))); + }); + } + + @Test + public void testAccumulate0() { + range(0, 1).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), 0), list.index(0).accumulate()); + }); + } + + @Test + public void testAccumulate1() { + range(0, 1).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), 0), list.index(0).accumulate(identity())); + }); + + } + + @Test + public void testAccumulate2() { + range(0, 2).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1)), list.index(0).accumulate(list.index(1), P::p)); + }); + } + + @Test + public void testAccumulate3() { + range(0, 3).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1, 2)), list.index(0).accumulate(list.index(1), list.index(2), P::p)); + }); + + } + + @Test + public void testAccumulate4() { + range(0, 4).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1, 2, 3)), list.index(0).accumulate(list.index(1), list.index(2), list.index(3), P::p)); + }); + + } + + @Test + public void testAccumulate5() { + range(0, 5).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1, 2, 3, 4)), list.index(0).accumulate(list.index(1), list.index(2), list.index(3), list.index(4), P::p)); + }); + } + + @Test + public void testAccumulate6() { + range(0, 6).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5)), list.index(0).accumulate(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), P::p)); + }); + } + + @Test + public void testAccumulate7() { + range(0, 7).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5, 6)), list.index(0).accumulate(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), P::p)); + }); + } + + @Test + public void testAccumulate8() { + range(0, 8).map(i -> List., Integer>>arrayList(fail(arrayList(String.valueOf(i))), success(i))) + .foldLeft(accumulator -> list -> accumulator.isEmpty() ? + list.map(List::single) : + accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) + .forEach(list -> { + assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5, 6, 7)), list.index(0).accumulate(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), list.index(7), P::p)); + }); + } + + @Test + public void testMap() { + assertEquals(Validation.fail("zero"), Validation.fail("zero").map(constant(0))); + assertEquals(Validation.success(0), Validation.success("zero").map(constant(0))); + assertEquals(Validation.fail("zero"), Validation.fail("zero").map(constant(0))); + assertEquals(Validation.success(0), Validation.success("zero").map(constant(0))); + } + + @Test + public void testBind() { + assertEquals(Validation.fail("zero"), Validation.fail("zero").bind(constant(Validation.fail("zero")))); + assertEquals(Validation.fail("zero"), Validation.success("zero").bind(constant(Validation.fail("zero")))); + assertEquals(Validation.fail("zero"), Validation.fail("zero").bind(constant(Validation.success(0)))); + assertEquals(Validation.success(0), Validation.success("zero").bind(constant(Validation.success(0)))); + } + @Test public void testSequenceEitherLeft() { assertEquals(left(fail("zero")), sequenceEitherLeft(fail("zero"))); @@ -55,10 +274,10 @@ public void testSequenceIO() throws IOException { @Test public void testSequenceList() { - assertEquals(List.single(fail("zero")), sequenceList(fail("zero"))); - assertEquals(List.nil(), sequenceList(success(List.nil()))); - assertEquals(List.single(success("zero")), sequenceList(success(List.single("zero")))); - assertEquals(List.arrayList(success("zero"), success("one")), sequenceList(success(List.arrayList("zero", "one")))); + assertEquals(single(fail("zero")), sequenceList(fail("zero"))); + assertEquals(nil(), sequenceList(success(nil()))); + assertEquals(single(success("zero")), sequenceList(success(single("zero")))); + assertEquals(arrayList(success("zero"), success("one")), sequenceList(success(arrayList("zero", "one")))); } @Test @@ -87,7 +306,7 @@ public void testSequenceSet() { assertEquals(Set.single(validationOrd(stringOrd, intOrd), fail("zero")), sequenceSet(stringOrd, intOrd, fail("zero"))); assertEquals(Set.empty(validationOrd(stringOrd, intOrd)), sequenceSet(stringOrd, intOrd, success(Set.empty(intOrd)))); assertEquals(Set.single(validationOrd(intOrd, stringOrd), success("zero")), sequenceSet(intOrd, stringOrd, success(Set.single(stringOrd, "zero")))); - assertEquals(Set.arraySet(validationOrd(intOrd, stringOrd), success("zero"), success("one")), sequenceSet(intOrd, stringOrd, Validation.success(Set.arraySet(stringOrd, "zero", "one")))); + assertEquals(Set.arraySet(validationOrd(intOrd, stringOrd), success("zero"), success("one")), sequenceSet(intOrd, stringOrd, success(Set.arraySet(stringOrd, "zero", "one")))); } @Test @@ -141,12 +360,12 @@ public void testTraverseIO() throws IOException { @Test public void testTraverseList() { - assertEquals(List.single(fail("zero")), fail("zero").traverseList(constant(List.nil()))); - assertEquals(List.nil(), success("zero").traverseList(constant(List.nil()))); - assertEquals(List.single(fail("zero")), fail("zero").traverseList(constant(List.single(0)))); - assertEquals(List.single(success(0)), success("zero").traverseList(constant(List.single(0)))); - assertEquals(List.single(fail("zero")), fail("zero").traverseList(constant(List.arrayList(0, 1)))); - assertEquals(List.arrayList(success(0), success(1)), success("zero").traverseList(constant(List.arrayList(0, 1)))); + assertEquals(single(fail("zero")), fail("zero").traverseList(constant(nil()))); + assertEquals(nil(), success("zero").traverseList(constant(nil()))); + assertEquals(single(fail("zero")), fail("zero").traverseList(constant(single(0)))); + assertEquals(single(success(0)), success("zero").traverseList(constant(single(0)))); + assertEquals(single(fail("zero")), fail("zero").traverseList(constant(arrayList(0, 1)))); + assertEquals(arrayList(success(0), success(1)), success("zero").traverseList(constant(arrayList(0, 1)))); } @Test From 4377adb82a2013d1faee7eac4c0f25821936ed43 Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Fri, 24 Jul 2020 16:37:05 -0400 Subject: [PATCH 110/173] Fixes #407. Add fj.data.Seq.bind(F> f). --- core/src/main/java/fj/data/Seq.java | 25 ++++++++++++++++--------- core/src/test/java/fj/data/SeqTest.java | 17 ++++++++++++++--- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/fj/data/Seq.java b/core/src/main/java/fj/data/Seq.java index abd20162..1ded05eb 100644 --- a/core/src/main/java/fj/data/Seq.java +++ b/core/src/main/java/fj/data/Seq.java @@ -1,20 +1,15 @@ package fj.data; import fj.*; +import fj.data.List.Buffer; +import fj.data.fingertrees.*; + +import java.util.*; import static fj.Bottom.error; import static fj.Monoid.intAdditionMonoid; import static fj.data.fingertrees.FingerTree.measured; -import fj.data.List.Buffer; -import fj.data.fingertrees.FingerTree; -import fj.data.fingertrees.MakeTree; -import fj.data.fingertrees.Measured; - -import java.util.AbstractList; -import java.util.Iterator; -import java.util.NoSuchElementException; - /** * Provides an immutable finite sequence, implemented as a finger tree. This structure gives O(1) access to * the head and tail, as well as O(log n) random access and concatenation of sequences. @@ -396,4 +391,16 @@ public Seq map(F f) { return new Seq<>(ftree.map(f, Seq.elemMeasured())); } + /** + * Bind the given function across this seq. + * + * @param f the given function + * @param the type of the seq value + * @return the seq + */ + public Seq bind(final F> f) { + return foldRight( + (element, accumulator) -> f.f(element).append(accumulator), + empty()); + } } diff --git a/core/src/test/java/fj/data/SeqTest.java b/core/src/test/java/fj/data/SeqTest.java index a7c85175..b19cb7d4 100644 --- a/core/src/test/java/fj/data/SeqTest.java +++ b/core/src/test/java/fj/data/SeqTest.java @@ -3,11 +3,10 @@ import fj.P2; import org.junit.Test; +import static fj.Function.constant; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * Created by MarkPerry on 16/01/2015. @@ -46,4 +45,16 @@ public void test() { assertThat(p2._2(), is(Seq.empty())); } + @Test + public void testBind() { + assertEquals(Seq.empty(), Seq.empty().bind(constant(Seq.empty()))); + assertEquals(Seq.empty(), Seq.empty().bind(constant(Seq.single(0)))); + assertEquals(Seq.empty(), Seq.empty().bind(constant(Seq.arraySeq(0, 1)))); + assertEquals(Seq.empty(), Seq.single("zero").bind(constant(Seq.empty()))); + assertEquals(Seq.single(0), Seq.single("zero").bind(constant(Seq.single(0)))); + assertEquals(Seq.arraySeq(0, 1), Seq.single("zero").bind(constant(Seq.arraySeq(0, 1)))); + assertEquals(Seq.empty(), Seq.arraySeq("zero", "one").bind(constant(Seq.empty()))); + assertEquals(Seq.arraySeq(0, 0), Seq.arraySeq("zero", "one").bind(constant(Seq.single(0)))); + assertEquals(Seq.arraySeq(0, 1, 0, 1), Seq.arraySeq("zero", "one").bind(constant(Seq.arraySeq(0, 1)))); + } } From 671b9d25e8d5a1db25252a889de06467b0696468 Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Fri, 24 Jul 2020 17:05:29 -0400 Subject: [PATCH 111/173] Fixes #408. Add fj.data.List.sequence* function, fj.data.List.traverse* functions. --- core/src/main/java/fj/data/List.java | 413 ++++++++++++++++++----- core/src/test/java/fj/data/ListTest.java | 218 +++++++++++- 2 files changed, 535 insertions(+), 96 deletions(-) diff --git a/core/src/main/java/fj/data/List.java b/core/src/main/java/fj/data/List.java index e0e12877..422d246b 100644 --- a/core/src/main/java/fj/data/List.java +++ b/core/src/main/java/fj/data/List.java @@ -1,54 +1,30 @@ package fj.data; -import static fj.Bottom.error; +import fj.*; +import fj.control.Trampoline; +import fj.control.parallel.*; +import fj.data.optic.Optional; +import fj.data.optic.*; +import fj.data.vector.V2; +import fj.function.Effect1; -import fj.Equal; -import fj.F2Functions; -import fj.Hash; -import fj.Monoid; -import fj.Ord; -import fj.Ordering; -import fj.P; -import fj.P1; -import fj.Semigroup; -import fj.Show; -import fj.Unit; -import fj.P2; -import fj.F0; -import fj.F; -import fj.F2; -import fj.Function; +import java.lang.Class; +import java.util.*; +import static fj.Bottom.error; import static fj.Function.*; -import static fj.P.p; -import static fj.P.p2; +import static fj.Ord.intOrd; +import static fj.Ordering.GT; +import static fj.P.*; import static fj.Unit.unit; import static fj.data.Array.mkArray; +import static fj.data.Either.*; import static fj.data.List.Buffer.*; -import static fj.data.Option.none; -import static fj.data.Option.some; +import static fj.data.Option.*; import static fj.data.optic.Optional.optional; import static fj.data.optic.Prism.prism; import static fj.data.vector.V.v; import static fj.function.Booleans.not; -import static fj.Ordering.GT; -import static fj.Ord.intOrd; - - -import fj.control.Trampoline; -import fj.control.parallel.Promise; -import fj.control.parallel.Strategy; -import fj.data.optic.Optional; -import fj.data.optic.PTraversal; -import fj.data.optic.Prism; -import fj.data.optic.Traversal; -import fj.data.vector.V2; -import fj.function.Effect1; - -import java.util.AbstractCollection; -import java.util.Collection; -import java.util.Iterator; -import java.util.NoSuchElementException; /** * Provides an in-memory, immutable, singly linked list. @@ -604,61 +580,315 @@ public final List sequence(final List bs) { return bind(c); } - /** - * Traverses through the List with the given function - * - * @param f The function that produces Option value - * @return none if applying f returns none to any element of the list or f mapped list in some . - */ - public final Option> traverseOption(final F> f) { - return foldRight( - (a, obs) -> f.f(a).bind(o -> obs.map(os -> os.cons(o))), - some(List.nil()) - ); - } + /** + * Sequence the given list and collect the output on the right side of an either. + * + * @param list the given list + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static final Either> sequenceEither(final List> list) { + return list.traverseEither(identity()); + } - /** - * Traverse through the List with given function. - * - * @param f The function that produces Either value. - * @return error in left or f mapped list in right. - */ - public final Either> traverseEither(final F> f) { - return foldRight( - (a, acc) -> f.f(a).right().bind(e -> acc.right().map(es -> es.cons(e))), - Either.right(List.nil()) - ); - } + /** + * Sequence the given list and collect the output on the left side of an either. + * + * @param list the given list + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static final Either, R> sequenceEitherLeft(final List> list) { + return list.traverseEitherLeft(identity()); + } - public final Stream> traverseStream(final F> f) { - return foldRight( - (a, acc) -> f.f(a).bind(s -> acc.map(ss -> ss.cons(s))), - Stream.nil() - ); - } + /** + * Sequence the given list and collect the output on the right side of an either. + * + * @param list the given list + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static final Either> sequenceEitherRight(final List> list) { + return list.traverseEitherRight(identity()); + } - public final P1> traverseP1(final F> f){ - return foldRight( - (a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))), - p(List.nil()) - ); - } + /** + * Sequence the given list and collect the output as a function. + * + * @param list the given list + * @param the type of the input value + * @param the type of the output value + * @return the either + */ + public static final F> sequenceF(final List> list) { + return list.traverseF(identity()); + } - public final IO> traverseIO(F> f) { - return this.foldRight( - (a, acc) -> IOFunctions.bind(f.f(a), b -> IOFunctions.map(acc, bs -> bs.cons(b))), - IOFunctions.unit(List.nil()) - ); - } + /** + * Sequence the given list and collect the output as an IO. + * + * @param list the given list + * @param the type of the IO value + * @return the IO + */ + public static final IO> sequenceIO(final List> list) { + return list.traverseIO(identity()); + } + + /** + * Sequence the given list and collect the output as an list. + * + * @param list the given list + * @param the type of the list value + * @return the list + */ + public static final List> sequenceList(final List> list) { + return list.traverseList(identity()); + } + + /** + * Sequence the given list and collect the output as an list. + * + * @param list the given list + * @param the type of the list value + * @return the list + */ + public static final Option> sequenceOption(final List> list) { + return list.traverseOption(identity()); + } + + /** + * Sequence the given list and collect the output as a P1. + * + * @param list the given list + * @param the type of the P1 value + * @return the P1 + */ + public static final P1> sequenceP1(final List> list) { + return list.traverseP1(identity()); + } + + /** + * Sequence the given list and collect the output as a seq. + * + * @param list the given list + * @param the type of the seq value + * @return the seq + */ + public static final Seq> sequenceSeq(final List> list) { + return list.traverseSeq(identity()); + } + + /** + * Sequence the given list and collect the output as a set; use the given ord to order the set. + * + * @param ord the given ord + * @param list the given list + * @param the type of the set value + * @return the either + */ + public static final Set> sequenceSet(final Ord ord, final List> list) { + return list.traverseSet(ord, identity()); + } + + /** + * Sequence the given list and collect the output as a stream. + * + * @param list the given list + * @param the type of the stream value + * @return the stream + */ + public static final Stream> sequenceStream(final List> list) { + return list.traverseStream(identity()); + } + + /** + * Sequence the given list and collect the output as a trampoline. + * + * @param list the given trampoline + * @param the type of the stream value + * @return the stream + */ + public static final Trampoline> sequenceTrampoline(final List> list) { + return list.traverseTrampoline(identity()); + } + + /** + * Sequence the given list and collect the output as a validation; use the given semigroup to reduce the errors. + * + * @param semigroup the given semigroup + * @param list the given list + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public static final Validation> sequenceValidation(final Semigroup semigroup, final List> list) { + return list.traverseValidation(semigroup, identity()); + } + + /** + * Traverse through the List with given function. + * + * @param f The function that produces Either value. + * @return error in left or f mapped list in right. + */ + public final Either> traverseEither(final F> f) { + return foldRight( + (a, acc) -> f.f(a).right().bind(e -> acc.right().map(es -> es.cons(e))), + Either.right(List.nil()) + ); + } + + /** + * Traverse this list with the given function and collect the output on the left side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public final Either, R> traverseEitherLeft(final F> f) { + return foldRight( + (element, either) -> f.f(element).left().bind(elementInner -> either.left().map(list -> list.cons(elementInner))), + left(nil())); + } + /** + * Traverse this list with the given function and collect the output on the right side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public final Either> traverseEitherRight(final F> f) { + return foldRight( + (element, either) -> f.f(element).right().bind(elementInner -> either.right().map(list -> list.cons(elementInner))), + right(nil())); + } + + /** + * Traverse this list with the given function and collect the output as a function. + * + * @param f the given function + * @param the type of the input value + * @param the type of the output value + * @return the function + */ public final F> traverseF(F> f) { return this.foldRight( (a, acc) -> Function.bind(acc, (bs) -> Function.compose(bs::cons, f.f(a))), constant(List.nil()) - ); + ); + } + + /** + * Traverse this list with the given function and collect the output as an IO. + * + * @param f the given function + * @param the type of the IO value + * @return the IO + */ + public final IO> traverseIO(F> f) { + return this.foldRight( + (a, acc) -> IOFunctions.bind(f.f(a), b -> IOFunctions.map(acc, bs -> bs.cons(b))), + IOFunctions.unit(List.nil()) + ); + } + + /** + * Traverse this list with the given function and collect the output as a list. + * + * @param f the given function + * @param the type of the list value + * @return the list + */ + public final List> traverseList(final F> f) { + return foldRight( + (a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))), + single(List.nil())); } + /** + * Traverses through the List with the given function + * + * @param f The function that produces Option value + * @return none if applying f returns none to any element of the list or f mapped list in some . + */ + public final Option> traverseOption(final F> f) { + return foldRight( + (a, obs) -> f.f(a).bind(o -> obs.map(os -> os.cons(o))), + some(List.nil()) + ); + } + + /** + * Traverse this list with the given function and collect the output as a p1. + * + * @param f the given function + * @param the type of the p1 value + * @return the p1 + */ + public final P1> traverseP1(final F> f) { + return foldRight( + (a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))), + p(List.nil()) + ); + } + + /** + * Traverse this list with the given function and collect the output as a seq. + * + * @param f the given function + * @param the type of the seq value + * @return the seq + */ + public final Seq> traverseSeq(final F> f) { + return foldRight( + (element, seq) -> f.f(element).bind(elementInner -> seq.map(list -> list.cons(elementInner))), + Seq.single(nil())); + } + + /** + * Traverse this list with the given function and collect the output as a set; use the given ord to order the set. + * + * @param ord the given ord + * @param f the given function + * @param the type of the set value + * @return the set + */ + public final Set> traverseSet(final Ord ord, final F> f) { + final Ord> listOption = Ord.listOrd(ord); + return foldRight( + (element, set) -> f.f(element).bind(listOption, elementInner -> set.map(listOption, list -> list.cons(elementInner))), + Set.single(listOption, nil())); + } + + /** + * Traverse this list with the given function and collect the output as a stream. + * + * @param f the given function + * @param the type of the stream value + * @return the stream + */ + public final Stream> traverseStream(final F> f) { + return foldRight( + (a, acc) -> f.f(a).bind(s -> acc.map(ss -> ss.cons(s))), + Stream.single(nil())); + } + + /** + * Traverse this list with the given function and collect the output as a trampoline. + * + * @param f the given function + * @param the type of the trampoline value + * @return the trampoline + */ public final Trampoline> traverseTrampoline(final F> f) { return foldRight( (a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))), @@ -671,12 +901,15 @@ public final Promise> traversePromise(final F> f) { Promise.promise(Strategy.idStrategy(), p(List.nil()))); } - public final List> traverseList(final F> f) { - return foldRight( - (a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))), - single(List.nil())); - } - + /** + * Traverse this list with the given function and collect the output as a validation; use the given semigroup to reduce the errors. + * + * @param s the given semigroup + * @param f the given function + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ public final Validation> traverseValidation(Semigroup s, final F> f) { return Validation.sequence(s, map(f)); } @@ -1474,7 +1707,7 @@ public final A maximum(final Ord o) { public final Option maximumOption(final Ord o) { return NonEmptyList.fromList(this).map(nel -> nel.maximum(o)); } - + /** * Returns the minimum element in this list according to the given ordering. * @@ -1484,7 +1717,7 @@ public final Option maximumOption(final Ord o) { public final A minimum(final Ord o) { return foldLeft1(o::min); } - + /** * Returns the minimum element in this list according to the given ordering. * diff --git a/core/src/test/java/fj/data/ListTest.java b/core/src/test/java/fj/data/ListTest.java index 3b720fe6..ca7d0f22 100644 --- a/core/src/test/java/fj/data/ListTest.java +++ b/core/src/test/java/fj/data/ListTest.java @@ -1,16 +1,36 @@ package fj.data; -import fj.Equal; -import fj.P2; +import fj.*; +import fj.control.Trampoline; import org.junit.Test; +import java.io.IOException; import java.util.Arrays; +import static fj.Function.constant; +import static fj.Ord.*; +import static fj.P.*; +import static fj.Semigroup.listSemigroup; +import static fj.data.Either.*; +import static fj.data.List.sequenceEither; +import static fj.data.List.sequenceEitherLeft; +import static fj.data.List.sequenceEitherRight; +import static fj.data.List.sequenceF; +import static fj.data.List.sequenceIO; +import static fj.data.List.sequenceList; +import static fj.data.List.sequenceOption; +import static fj.data.List.sequenceP1; +import static fj.data.List.sequenceSeq; +import static fj.data.List.sequenceSet; +import static fj.data.List.sequenceStream; +import static fj.data.List.sequenceTrampoline; +import static fj.data.List.sequenceValidation; +import static fj.data.List.*; +import static fj.data.Option.*; +import static fj.data.Validation.fail; +import static fj.data.Validation.*; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * Created by MarkPerry on 16/01/2015. @@ -91,4 +111,190 @@ public void array() { assertThat(List.range(1, max + 1).array(Integer[].class), equalTo(ints)); } + @Test + public void testSequenceEither() { + assertEquals(right(nil()), sequenceEither(nil())); + assertEquals(right(single("zero")), sequenceEither(single(right("zero")))); + assertEquals(left("zero"), sequenceEither(single(left("zero")))); + } + + @Test + public void testSequenceEitherLeft() { + assertEquals(left(nil()), sequenceEitherLeft(nil())); + assertEquals(left(single("zero")), sequenceEitherLeft(single(left("zero")))); + assertEquals(right("zero"), sequenceEitherLeft(single(right("zero")))); + } + + @Test + public void testSequenceEitherRight() { + assertEquals(right(nil()), sequenceEitherRight(nil())); + assertEquals(right(single("zero")), sequenceEitherRight(single(right("zero")))); + assertEquals(left("zero"), sequenceEitherRight(single(left("zero")))); + } + + @Test + public void testSequenceF() { + assertEquals(constant(nil()).f(1), sequenceF(nil()).f(1)); + assertEquals(constant(single("zero")).f(1), sequenceF(single(constant("zero"))).f(1)); + } + + @Test + public void testSequenceIO() throws IOException { + assertEquals(IOFunctions.lazy(constant(nil())).run(), sequenceIO(nil()).run()); + assertEquals(IOFunctions.lazy(constant(single("zero"))).run(), sequenceIO(single(IOFunctions.lazy(constant("zero")))).run()); + } + + @Test + public void testSequenceList() { + assertEquals(single(nil()), sequenceList(nil())); + assertEquals(List.nil(), sequenceList(single(nil()))); + assertEquals(single(single("zero")), sequenceList(single(single("zero")))); + assertEquals(List.arrayList(single("zero"), single("one")), sequenceList(single(arrayList("zero", "one")))); + } + + @Test + public void testSequenceOption() { + assertEquals(some(nil()), sequenceOption(nil())); + assertEquals(none(), sequenceOption(single(none()))); + assertEquals(some(single("zero")), sequenceOption(single(some("zero")))); + } + + @Test + public void testSequenceP1() { + assertEquals(p(nil()), sequenceP1(nil())); + assertEquals(p(single("zero")), sequenceP1(single(p("zero")))); + } + + @Test + public void testSequenceSeq() { + assertEquals(Seq.single(nil()), sequenceSeq(nil())); + assertEquals(Seq.empty(), sequenceSeq(single(Seq.empty()))); + assertEquals(Seq.single(single("zero")), sequenceSeq(single(Seq.single("zero")))); + assertEquals(Seq.arraySeq(single("zero"), single("one")), sequenceSeq(single(Seq.arraySeq("zero", "one")))); + } + + @Test + public void testSequenceSet() { + assertEquals(Set.arraySet(listOrd(stringOrd), nil()), sequenceSet(stringOrd, nil())); + assertEquals(Set.empty(listOrd(stringOrd)), sequenceSet(stringOrd, single(Set.empty(stringOrd)))); + assertEquals(Set.arraySet(listOrd(stringOrd), single("zero")), sequenceSet(stringOrd, single(Set.single(stringOrd, "zero")))); + assertEquals(Set.arraySet(listOrd(stringOrd), single("zero"), single("one")), sequenceSet(stringOrd, single(Set.arraySet(stringOrd, "zero", "one")))); + } + + @Test + public void testSequenceStream() { + assertEquals(Stream.single(nil()), sequenceStream(nil())); + assertEquals(Stream.nil(), sequenceStream(single(Stream.nil()))); + assertEquals(Stream.single(single("zero")), sequenceStream(single(Stream.single("zero")))); + assertEquals(Stream.arrayStream(single("zero"), single("one")), sequenceStream(single(Stream.arrayStream("zero", "one")))); + } + + @Test + public void testSequenceTrampoline() { + assertEquals(Trampoline.pure(nil()).run(), sequenceTrampoline(nil()).run()); + assertEquals(Trampoline.pure(single(0)).run(), sequenceTrampoline(single(Trampoline.pure(0))).run()); + } + + @Test + public void testSequenceValidation() { + assertEquals(Validation.success(nil()), sequenceValidation(listSemigroup(), nil())); + assertEquals(Validation.fail(single(0)), sequenceValidation(listSemigroup(), single(Validation.fail(single(0))))); + assertEquals(Validation.success(single(0)), sequenceValidation(listSemigroup(), single(Validation.success(0)))); + } + + @Test + public void testTraverseEitherLeft() { + assertEquals(left(nil()), nil().traverseEitherLeft(constant(left(0)))); + assertEquals(left(single(0)), single("zero").traverseEitherLeft(constant(left(0)))); + assertEquals(left(nil()), nil().traverseEitherLeft(constant(right(0)))); + assertEquals(right(0), single("zero").traverseEitherLeft(constant(right(0)))); + } + + @Test + public void testTraverseEitherRight() { + assertEquals(right(nil()), nil().traverseEitherRight(constant(right(0)))); + assertEquals(right(single(0)), single("zero").traverseEitherRight(constant(right(0)))); + assertEquals(right(nil()), nil().traverseEitherRight(constant(left(0)))); + assertEquals(left(0), single("zero").traverseEitherRight(constant(left(0)))); + } + + @Test + public void testTraverseF() { + assertEquals(constant(nil()).f(1), nil().traverseF(constant(constant(0))).f(1)); + assertEquals(constant(single(0)).f(1), single("zero").traverseF(constant(constant(0))).f(1)); + } + + @Test + public void testTraverseIO() throws IOException { + assertEquals(IOFunctions.lazy(constant(nil())).run(), nil().traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + assertEquals(IOFunctions.lazy(constant(single(0))).run(), single("zero").traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + } + + @Test + public void testTraverseList() { + assertEquals(single(nil()), nil().traverseList(constant(List.nil()))); + assertEquals(List.nil(), single("zero").traverseList(constant(List.nil()))); + assertEquals(single(nil()), nil().traverseList(constant(single(0)))); + assertEquals(single(single(0)), single("zero").traverseList(constant(single(0)))); + assertEquals(single(nil()), nil().traverseList(constant(List.arrayList(0, 1)))); + assertEquals(List.arrayList(single(0), single(1)), single("zero").traverseList(constant(List.arrayList(0, 1)))); + } + + @Test + public void testTraverseOption() { + assertEquals(some(nil()), nil().traverseOption(constant(none()))); + assertEquals(none(), single("zero").traverseOption(constant(none()))); + assertEquals(some(nil()), nil().traverseOption(constant(some(0)))); + assertEquals(some(single(0)), single("zero").traverseOption(constant(some(0)))); + } + + @Test + public void testTraverseP1() { + assertEquals(p(nil()), nil().traverseP1(constant(p(0)))); + assertEquals(p(single(0)), single("zero").traverseP1(constant(p(0)))); + } + + @Test + public void testTraverseSeq() { + assertEquals(Seq.single(nil()), nil().traverseSeq(constant(Seq.empty()))); + assertEquals(Seq.empty(), single("zero").traverseSeq(constant(Seq.empty()))); + assertEquals(Seq.single(nil()), nil().traverseSeq(constant(Seq.single(0)))); + assertEquals(Seq.single(single(0)), single("zero").traverseSeq(constant(Seq.single(0)))); + assertEquals(Seq.single(nil()), nil().traverseSeq(constant(Seq.arraySeq(0, 1)))); + assertEquals(Seq.arraySeq(single(0), single(1)), single("zero").traverseSeq(constant(Seq.arraySeq(0, 1)))); + } + + @Test + public void testTraverseSet() { + assertEquals(Set.arraySet(listOrd(intOrd), nil()), nil().traverseSet(intOrd, constant(Set.empty(intOrd)))); + assertEquals(Set.empty(listOrd(intOrd)), single("zero").traverseSet(intOrd, constant(Set.empty(intOrd)))); + assertEquals(Set.single(listOrd(intOrd), nil()), nil().traverseSet(intOrd, constant(Set.single(intOrd, 0)))); + assertEquals(Set.single(listOrd(intOrd), single(0)), single("zero").traverseSet(intOrd, constant(Set.single(intOrd, 0)))); + assertEquals(Set.single(listOrd(intOrd), nil()), nil().traverseSet(intOrd, constant(Set.arraySet(intOrd, 0, 1)))); + assertEquals(Set.arraySet(listOrd(intOrd), single(0), single(1)), single("zero").traverseSet(intOrd, constant(Set.arraySet(intOrd, 0, 1)))); + } + + @Test + public void testTraverseStream() { + assertEquals(Stream.single(nil()), nil().traverseStream(constant(Stream.nil()))); + assertEquals(Stream.nil(), single("zero").traverseStream(constant(Stream.nil()))); + assertEquals(Stream.single(nil()), nil().traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.single(single(0)), single("zero").traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.single(nil()), nil().traverseStream(constant(Stream.arrayStream(0, 1)))); + assertEquals(Stream.arrayStream(single(0), single(1)), single("zero").traverseStream(constant(Stream.arrayStream(0, 1)))); + } + + @Test + public void testTraverseTrampoline() { + assertEquals(Trampoline.pure(nil()).run(), nil().traverseTrampoline(constant(Trampoline.pure(0))).run()); + assertEquals(Trampoline.pure(single(0)).run(), single("zero").traverseTrampoline(constant(Trampoline.pure(0))).run()); + } + + @Test + public void testTraverseValidation() { + assertEquals(success(nil()), nil().traverseValidation(listSemigroup(), constant(Validation.fail(single(0))))); + assertEquals(fail(single(0)), single("zero").traverseValidation(listSemigroup(), constant(Validation.fail(single(0))))); + assertEquals(success(nil()), nil().traverseValidation(listSemigroup(), constant(Validation.success(0)))); + assertEquals(success(single(0)), single("zero").traverseValidation(listSemigroup(), constant(Validation.success(0)))); + } } From 24779066804769cd93a7e4cc4236b44836e43074 Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Fri, 24 Jul 2020 23:39:07 -0400 Subject: [PATCH 112/173] Fixes #412. Add fj.data.Seq.sequence*, fj.data.Seq.traverse* functions. --- core/src/main/java/fj/Ord.java | 51 ++-- core/src/main/java/fj/data/Seq.java | 361 ++++++++++++++++++++++++ core/src/test/java/fj/data/SeqTest.java | 251 +++++++++++++++- 3 files changed, 638 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/fj/Ord.java b/core/src/main/java/fj/Ord.java index 0a778f4d..eef729c4 100644 --- a/core/src/main/java/fj/Ord.java +++ b/core/src/main/java/fj/Ord.java @@ -1,23 +1,11 @@ package fj; -import fj.data.Array; -import fj.data.Either; -import fj.data.List; -import fj.data.Natural; -import fj.data.NonEmptyList; -import fj.data.Option; -import fj.data.Set; -import fj.data.Stream; -import fj.data.Validation; - -import java.math.BigDecimal; -import java.math.BigInteger; +import fj.data.*; + +import java.math.*; import java.util.Comparator; -import static fj.Function.apply; -import static fj.Function.compose; -import static fj.Function.curry; -import static fj.Semigroup.semigroup; +import static fj.Function.*; import static fj.Semigroup.semigroupDef; /** @@ -541,6 +529,37 @@ public static Ord> listOrd(final Ord oa) { }); } + /** + * Return a seq ord using the given value ord. + * + * @param ord the given value ord + * @param the type of the seq value + * @return the seq ord + */ + public static Ord> seqOrd(final Ord ord) { + return ordDef((l1, l2) -> { + Seq x1 = l1; + Seq x2 = l2; + + while (x1.isNotEmpty() && x2.isNotEmpty()) { + final Ordering o = ord.compare(x1.head(), x2.head()); + if (o == Ordering.LT || o == Ordering.GT) { + return o; + } + x1 = x1.tail(); + x2 = x2.tail(); + } + + if (x1.isEmpty() && x2.isEmpty()) { + return Ordering.EQ; + } else if (x1.isEmpty()) { + return Ordering.LT; + } else { + return Ordering.GT; + } + }); + } + /** * An order instance for the {@link NonEmptyList} type. * diff --git a/core/src/main/java/fj/data/Seq.java b/core/src/main/java/fj/data/Seq.java index 1ded05eb..3bcb7033 100644 --- a/core/src/main/java/fj/data/Seq.java +++ b/core/src/main/java/fj/data/Seq.java @@ -1,13 +1,19 @@ package fj.data; import fj.*; +import fj.control.Trampoline; import fj.data.List.Buffer; import fj.data.fingertrees.*; import java.util.*; import static fj.Bottom.error; +import static fj.Function.*; import static fj.Monoid.intAdditionMonoid; +import static fj.P.p; +import static fj.data.Either.*; +import static fj.data.Option.some; +import static fj.data.Validation.success; import static fj.data.fingertrees.FingerTree.measured; /** @@ -403,4 +409,359 @@ public Seq bind(final F> f) { (element, accumulator) -> f.f(element).append(accumulator), empty()); } + + /** + * Sequence the given seq and collect the output on the right side of an either. + * + * @param seq the given seq + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static Either> sequenceEither(final Seq> seq) { + return seq.traverseEither(identity()); + } + + /** + * Sequence the given seq and collect the output on the left side of an either. + * + * @param seq the given seq + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static Either, R> sequenceEitherLeft(final Seq> seq) { + return seq.traverseEitherLeft(identity()); + } + + /** + * Sequence the given seq and collect the output on the right side of an either. + * + * @param seq the given seq + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static Either> sequenceEitherRight(final Seq> seq) { + return seq.traverseEitherRight(identity()); + } + + /** + * Sequence the given seq and collect the output as a function. + * + * @param seq the given seq + * @param the type of the input value + * @param the type of the output value + * @return the either + */ + public static F> sequenceF(final Seq> seq) { + return seq.traverseF(identity()); + } + + /** + * Sequence the given seq and collect the output as an IO. + * + * @param seq the given seq + * @param the type of the IO value + * @return the IO + */ + public static IO> sequenceIO(final Seq> seq) { + return seq.traverseIO(identity()); + } + + /** + * Sequence the given seq and collect the output as a list. + * + * @param seq the given seq + * @param the type of the seq value + * @return the list + */ + public static List> sequenceList(final Seq> seq) { + return seq.traverseList(identity()); + } + + /** + * Sequence the given seq and collect the output as an seq. + * + * @param seq the given seq + * @param the type of the seq value + * @return the seq + */ + public static Option> sequenceOption(final Seq> seq) { + return seq.traverseOption(identity()); + } + + /** + * Sequence the given seq and collect the output as a P1. + * + * @param seq the given seq + * @param the type of the P1 value + * @return the P1 + */ + public static P1> sequenceP1(final Seq> seq) { + return seq.traverseP1(identity()); + } + + /** + * Sequence the given seq and collect the output as a seq. + * + * @param seq the given seq + * @param the type of the seq value + * @return the seq + */ + public static Seq> sequenceSeq(final Seq> seq) { + return seq.traverseSeq(identity()); + } + + /** + * Sequence the given seq and collect the output as a set; use the given ord to order the set. + * + * @param ord the given ord + * @param seq the given seq + * @param the type of the set value + * @return the either + */ + public static Set> sequenceSet(final Ord ord, final Seq> seq) { + return seq.traverseSet(ord, identity()); + } + + /** + * Sequence the given seq and collect the output as a stream. + * + * @param seq the given seq + * @param the type of the stream value + * @return the stream + */ + public static Stream> sequenceStream(final Seq> seq) { + return seq.traverseStream(identity()); + } + + /** + * Sequence the given seq and collect the output as a trampoline. + * + * @param seq the given trampoline + * @param the type of the stream value + * @return the stream + */ + public static Trampoline> sequenceTrampoline(final Seq> seq) { + return seq.traverseTrampoline(identity()); + } + + /** + * Sequence the given seq and collect the output as a validation. + * + * @param seq the given seq + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public static Validation> sequenceValidation(final Seq> seq) { + return seq.traverseValidation(identity()); + } + + /** + * Sequence the given seq and collect the output as a validation; use the given semigroup to reduce the errors. + * + * @param semigroup the given semigroup + * @param seq the given seq + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public static Validation> sequenceValidation(final Semigroup semigroup, final Seq> seq) { + return seq.traverseValidation(semigroup, identity()); + } + + /** + * Traverse this seq with the given function and collect the output on the right side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public Either> traverseEither(final F> f) { + return traverseEitherRight(f); + } + + /** + * Traverse this seq with the given function and collect the output on the left side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public Either, R> traverseEitherLeft(final F> f) { + return foldRight( + (element, either) -> f.f(element).left().bind(elementInner -> either.left().map(seq -> seq.cons(elementInner))), + left(empty())); + } + + /** + * Traverse this seq with the given function and collect the output on the right side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public Either> traverseEitherRight(final F> f) { + return foldRight( + (element, either) -> f.f(element).right().bind(elementInner -> either.right().map(seq -> seq.cons(elementInner))), + right(empty())); + } + + /** + * Traverse this seq with the given function and collect the output as a function. + * + * @param f the given function + * @param the type of the input value + * @param the type of the output value + * @return the function + */ + public F> traverseF(final F> f) { + return foldRight( + (element, fInner) -> Function.bind(f.f(element), elementInner -> andThen(fInner, seq -> seq.cons(elementInner))), + constant(empty())); + } + + /** + * Traverse this seq with the given function and collect the output as an IO. + * + * @param f the given function + * @param the type of the IO value + * @return the IO + */ + public IO> traverseIO(final F> f) { + return foldRight( + (element, io) -> IOFunctions.bind(f.f(element), elementInner -> IOFunctions.map(io, seq -> seq.cons(elementInner))), + IOFunctions.unit(empty()) + ); + } + + /** + * Traverse this seq with the given function and collect the output as a list. + * + * @param f the given function + * @param the type of the list value + * @return the list + */ + public List> traverseList(final F> f) { + return foldRight( + (element, list) -> f.f(element).bind(elementInner -> list.map(seq -> seq.cons(elementInner))), + List.single(empty())); + } + + /** + * Traverses through the Seq with the given function + * + * @param f The function that produces Option value + * @return none if applying f returns none to any element of the seq or f mapped seq in some . + */ + public Option> traverseOption(final F> f) { + return foldRight( + (element, option) -> f.f(element).bind(elementInner -> option.map(seq -> seq.cons(elementInner))), + some(empty()) + ); + } + + /** + * Traverse this seq with the given function and collect the output as a p1. + * + * @param f the given function + * @param the type of the p1 value + * @return the p1 + */ + public P1> traverseP1(final F> f) { + return foldRight( + (element, p1) -> f.f(element).bind(elementInner -> p1.map(seq -> seq.cons(elementInner))), + p(empty()) + ); + } + + /** + * Traverse this seq with the given function and collect the output as a seq. + * + * @param f the given function + * @param the type of the seq value + * @return the seq + */ + public Seq> traverseSeq(final F> f) { + return foldRight( + (element, seq) -> f.f(element).bind(elementInner -> seq.map(seqInner -> seqInner.cons(elementInner))), + single(empty())); + } + + /** + * Traverse this seq with the given function and collect the output as a set; use the given ord to order the set. + * + * @param ord the given ord + * @param f the given function + * @param the type of the set value + * @return the set + */ + public Set> traverseSet(final Ord ord, final F> f) { + final Ord> seqOrd = Ord.seqOrd(ord); + return foldRight( + (element, set) -> f.f(element).bind(seqOrd, elementInner -> set.map(seqOrd, seq -> seq.cons(elementInner))), + Set.single(seqOrd, empty())); + } + + /** + * Traverse this seq with the given function and collect the output as a stream. + * + * @param f the given function + * @param the type of the stream value + * @return the stream + */ + public Stream> traverseStream(final F> f) { + return foldRight( + (element, stream) -> f.f(element).bind(elementInner -> stream.map(seq -> seq.cons(elementInner))), + Stream.single(empty())); + } + + /** + * Traverse this seq with the given function and collect the output as a trampoline. + * + * @param f the given function + * @param the type of the trampoline value + * @return the trampoline + */ + public Trampoline> traverseTrampoline(final F> f) { + return foldRight( + (element, trampoline) -> f.f(element).bind(elementInner -> trampoline.map(seq -> seq.cons(elementInner))), + Trampoline.pure(empty())); + } + + /** + * Traverse this seq with the given function and collect the output as a validation. + * + * @param f the given function + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public Validation> traverseValidation(final F> f) { + return foldRight( + (element, validation) -> f.f(element).bind(elementInner -> validation.map(seq -> seq.cons(elementInner))), + success(empty()) + ); + } + + /** + * Traverse this seq with the given function and collect the output as a validation; use the given semigroup to reduce the errors. + * + * @param semigroup the given semigroup + * @param f the given function + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public Validation> traverseValidation(final Semigroup semigroup, final F> f) { + return foldRight( + (element, validation) -> f.f(element).map(Seq::single).accumulate(semigroup, validation, Seq::append), + success(empty()) + ); + } } diff --git a/core/src/test/java/fj/data/SeqTest.java b/core/src/test/java/fj/data/SeqTest.java index b19cb7d4..0b62db00 100644 --- a/core/src/test/java/fj/data/SeqTest.java +++ b/core/src/test/java/fj/data/SeqTest.java @@ -1,9 +1,34 @@ package fj.data; import fj.P2; +import fj.control.Trampoline; import org.junit.Test; +import java.io.IOException; + import static fj.Function.constant; +import static fj.Ord.*; +import static fj.P.p; +import static fj.Semigroup.listSemigroup; +import static fj.data.Either.*; +import static fj.data.List.arrayList; +import static fj.data.Option.*; +import static fj.data.Seq.sequenceEither; +import static fj.data.Seq.sequenceEitherLeft; +import static fj.data.Seq.sequenceEitherRight; +import static fj.data.Seq.sequenceF; +import static fj.data.Seq.sequenceIO; +import static fj.data.Seq.sequenceList; +import static fj.data.Seq.sequenceOption; +import static fj.data.Seq.sequenceP1; +import static fj.data.Seq.sequenceSeq; +import static fj.data.Seq.sequenceSet; +import static fj.data.Seq.sequenceStream; +import static fj.data.Seq.sequenceTrampoline; +import static fj.data.Seq.sequenceValidation; +import static fj.data.Seq.*; +import static fj.data.Validation.fail; +import static fj.data.Validation.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.junit.Assert.*; @@ -47,14 +72,222 @@ public void test() { @Test public void testBind() { - assertEquals(Seq.empty(), Seq.empty().bind(constant(Seq.empty()))); - assertEquals(Seq.empty(), Seq.empty().bind(constant(Seq.single(0)))); - assertEquals(Seq.empty(), Seq.empty().bind(constant(Seq.arraySeq(0, 1)))); - assertEquals(Seq.empty(), Seq.single("zero").bind(constant(Seq.empty()))); - assertEquals(Seq.single(0), Seq.single("zero").bind(constant(Seq.single(0)))); - assertEquals(Seq.arraySeq(0, 1), Seq.single("zero").bind(constant(Seq.arraySeq(0, 1)))); - assertEquals(Seq.empty(), Seq.arraySeq("zero", "one").bind(constant(Seq.empty()))); - assertEquals(Seq.arraySeq(0, 0), Seq.arraySeq("zero", "one").bind(constant(Seq.single(0)))); - assertEquals(Seq.arraySeq(0, 1, 0, 1), Seq.arraySeq("zero", "one").bind(constant(Seq.arraySeq(0, 1)))); + assertEquals(empty(), empty().bind(constant(empty()))); + assertEquals(empty(), empty().bind(constant(single(0)))); + assertEquals(empty(), empty().bind(constant(arraySeq(0, 1)))); + assertEquals(empty(), single("zero").bind(constant(empty()))); + assertEquals(single(0), single("zero").bind(constant(single(0)))); + assertEquals(arraySeq(0, 1), single("zero").bind(constant(arraySeq(0, 1)))); + assertEquals(empty(), arraySeq("zero", "one").bind(constant(empty()))); + assertEquals(arraySeq(0, 0), arraySeq("zero", "one").bind(constant(single(0)))); + assertEquals(arraySeq(0, 1, 0, 1), arraySeq("zero", "one").bind(constant(arraySeq(0, 1)))); + } + + @Test + public void testSequenceEither() { + assertEquals(right(empty()), sequenceEither(empty())); + assertEquals(right(single("zero")), sequenceEither(single(right("zero")))); + assertEquals(left("zero"), sequenceEither(single(left("zero")))); + } + + @Test + public void testSequenceEitherLeft() { + assertEquals(left(empty()), sequenceEitherLeft(empty())); + assertEquals(left(single("zero")), sequenceEitherLeft(single(left("zero")))); + assertEquals(right("zero"), sequenceEitherLeft(single(right("zero")))); + } + + @Test + public void testSequenceEitherRight() { + assertEquals(right(empty()), sequenceEitherRight(empty())); + assertEquals(right(single("zero")), sequenceEitherRight(single(right("zero")))); + assertEquals(left("zero"), sequenceEitherRight(single(left("zero")))); + } + + @Test + public void testSequenceF() { + assertEquals(constant(empty()).f(1), sequenceF(empty()).f(1)); + assertEquals(constant(single("zero")).f(1), sequenceF(single(constant("zero"))).f(1)); + } + + @Test + public void testSequenceIO() throws IOException { + assertEquals(IOFunctions.lazy(constant(empty())).run(), sequenceIO(empty()).run()); + assertEquals(IOFunctions.lazy(constant(single("zero"))).run(), sequenceIO(single(IOFunctions.lazy(constant("zero")))).run()); + } + + @Test + public void testSequenceList() { + assertEquals(List.single(empty()), sequenceList(empty())); + assertEquals(List.nil(), sequenceList(single(List.nil()))); + assertEquals(List.single(single("zero")), sequenceList(single(List.single("zero")))); + assertEquals(arrayList(single("zero"), single("one")), sequenceList(single(arrayList("zero", "one")))); + } + + @Test + public void testSequenceOption() { + assertEquals(some(empty()), sequenceOption(empty())); + assertEquals(none(), sequenceOption(single(none()))); + assertEquals(some(single("zero")), sequenceOption(single(some("zero")))); + } + + @Test + public void testSequenceP1() { + assertEquals(p(empty()), sequenceP1(empty())); + assertEquals(p(single("zero")), sequenceP1(single(p("zero")))); + } + + @Test + public void testSequenceSeq() { + assertEquals(single(empty()), sequenceSeq(empty())); + assertEquals(empty(), sequenceSeq(single(empty()))); + assertEquals(single(single("zero")), sequenceSeq(single(single("zero")))); + assertEquals(arraySeq(single("zero"), single("one")), sequenceSeq(single(arraySeq("zero", "one")))); + } + + @Test + public void testSequenceSet() { + assertEquals(Set.arraySet(seqOrd(stringOrd), empty()), sequenceSet(stringOrd, empty())); + assertEquals(Set.empty(seqOrd(stringOrd)), sequenceSet(stringOrd, single(Set.empty(stringOrd)))); + assertEquals(Set.arraySet(seqOrd(stringOrd), single("zero")), sequenceSet(stringOrd, single(Set.single(stringOrd, "zero")))); + assertEquals(Set.arraySet(seqOrd(stringOrd), single("zero"), single("one")), sequenceSet(stringOrd, single(Set.arraySet(stringOrd, "zero", "one")))); + } + + @Test + public void testSequenceStream() { + assertEquals(Stream.single(empty()), sequenceStream(empty())); + assertEquals(Stream.nil(), sequenceStream(single(Stream.nil()))); + assertEquals(Stream.single(single("zero")), sequenceStream(single(Stream.single("zero")))); + assertEquals(Stream.arrayStream(single("zero"), single("one")), sequenceStream(single(Stream.arrayStream("zero", "one")))); + } + + @Test + public void testSequenceTrampoline() { + assertEquals(Trampoline.pure(empty()).run(), sequenceTrampoline(empty()).run()); + assertEquals(Trampoline.pure(single(0)).run(), sequenceTrampoline(single(Trampoline.pure(0))).run()); + } + + @Test + public void testSequenceValidation() { + assertEquals(success(empty()), sequenceValidation(empty())); + assertEquals(fail(single(0)), sequenceValidation(single(fail(single(0))))); + assertEquals(success(single(0)), sequenceValidation(single(success(0)))); + } + + @Test + public void testSequenceValidationSemigroup() { + assertEquals(success(empty()), sequenceValidation(listSemigroup(), empty())); + assertEquals(fail(List.single(0)), sequenceValidation(listSemigroup(), single(fail(List.single(0))))); + assertEquals(success(single(0)), sequenceValidation(listSemigroup(), single(success(0)))); + } + + @Test + public void testTraverseEitherLeft() { + assertEquals(left(empty()), empty().traverseEitherLeft(constant(left(0)))); + assertEquals(left(single(0)), single("zero").traverseEitherLeft(constant(left(0)))); + assertEquals(left(empty()), empty().traverseEitherLeft(constant(right(0)))); + assertEquals(right(0), single("zero").traverseEitherLeft(constant(right(0)))); + } + + @Test + public void testTraverseEitherRight() { + assertEquals(right(empty()), empty().traverseEitherRight(constant(right(0)))); + assertEquals(right(single(0)), single("zero").traverseEitherRight(constant(right(0)))); + assertEquals(right(empty()), empty().traverseEitherRight(constant(left(0)))); + assertEquals(left(0), single("zero").traverseEitherRight(constant(left(0)))); + } + + @Test + public void testTraverseF() { + assertEquals(constant(empty()).f(1), empty().traverseF(constant(constant(0))).f(1)); + assertEquals(constant(single(0)).f(1), single("zero").traverseF(constant(constant(0))).f(1)); + } + + @Test + public void testTraverseIO() throws IOException { + assertEquals(IOFunctions.lazy(constant(empty())).run(), empty().traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + assertEquals(IOFunctions.lazy(constant(single(0))).run(), single("zero").traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + } + + @Test + public void testTraverseList() { + assertEquals(List.single(empty()), empty().traverseList(constant(List.nil()))); + assertEquals(List.nil(), single("zero").traverseList(constant(List.nil()))); + assertEquals(List.single(empty()), empty().traverseList(constant(List.single(0)))); + assertEquals(List.single(single(0)), single("zero").traverseList(constant(List.single(0)))); + assertEquals(List.single(empty()), empty().traverseList(constant(arrayList(0, 1)))); + assertEquals(arrayList(single(0), single(1)), single("zero").traverseList(constant(arrayList(0, 1)))); + } + + @Test + public void testTraverseOption() { + assertEquals(some(empty()), empty().traverseOption(constant(none()))); + assertEquals(none(), single("zero").traverseOption(constant(none()))); + assertEquals(some(empty()), empty().traverseOption(constant(some(0)))); + assertEquals(some(single(0)), single("zero").traverseOption(constant(some(0)))); + } + + @Test + public void testTraverseP1() { + assertEquals(p(empty()), empty().traverseP1(constant(p(0)))); + assertEquals(p(single(0)), single("zero").traverseP1(constant(p(0)))); + } + + @Test + public void testTraverseSeq() { + assertEquals(single(empty()), empty().traverseSeq(constant(empty()))); + assertEquals(empty(), single("zero").traverseSeq(constant(empty()))); + assertEquals(single(empty()), empty().traverseSeq(constant(single(0)))); + assertEquals(single(single(0)), single("zero").traverseSeq(constant(single(0)))); + assertEquals(single(empty()), empty().traverseSeq(constant(arraySeq(0, 1)))); + assertEquals(arraySeq(single(0), single(1)), single("zero").traverseSeq(constant(arraySeq(0, 1)))); + } + + @Test + public void testTraverseSet() { + assertEquals(Set.arraySet(seqOrd(intOrd), empty()), empty().traverseSet(intOrd, constant(Set.empty(intOrd)))); + assertEquals(Set.empty(seqOrd(intOrd)), single("zero").traverseSet(intOrd, constant(Set.empty(intOrd)))); + assertEquals(Set.single(seqOrd(intOrd), empty()), empty().traverseSet(intOrd, constant(Set.single(intOrd, 0)))); + assertEquals(Set.single(seqOrd(intOrd), single(0)), single("zero").traverseSet(intOrd, constant(Set.single(intOrd, 0)))); + assertEquals(Set.single(seqOrd(intOrd), empty()), empty().traverseSet(intOrd, constant(Set.arraySet(intOrd, 0, 1)))); + assertEquals(Set.arraySet(seqOrd(intOrd), single(0), single(1)), single("zero").traverseSet(intOrd, constant(Set.arraySet(intOrd, 0, 1)))); + } + + @Test + public void testTraverseStream() { + assertEquals(Stream.single(empty()), empty().traverseStream(constant(Stream.nil()))); + assertEquals(Stream.nil(), single("zero").traverseStream(constant(Stream.nil()))); + assertEquals(Stream.single(empty()), empty().traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.single(single(0)), single("zero").traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.single(empty()), empty().traverseStream(constant(Stream.arrayStream(0, 1)))); + assertEquals(Stream.arrayStream(single(0), single(1)), single("zero").traverseStream(constant(Stream.arrayStream(0, 1)))); + } + + @Test + public void testTraverseTrampoline() { + assertEquals(Trampoline.pure(empty()).run(), empty().traverseTrampoline(constant(Trampoline.pure(0))).run()); + assertEquals(Trampoline.pure(single(0)).run(), single("zero").traverseTrampoline(constant(Trampoline.pure(0))).run()); + } + + @Test + public void testTraverseValidation() { + assertEquals(success(empty()), empty().traverseValidation(constant(fail(single(0))))); + assertEquals(fail(single(0)), single("zero").traverseValidation(constant(fail(single(0))))); + assertEquals(success(empty()), empty().traverseValidation(constant(success(0)))); + assertEquals(success(single(0)), single("zero").traverseValidation(constant(success(0)))); + + assertEquals(success(arraySeq(0, 2, 4, 6, 8)), arraySeq(0, 2, 4, 6, 8).traverseValidation(i -> condition(i% 2 == 0, List.single(i), i))); + assertEquals(fail(List.single(1)), arraySeq(0, 1, 2, 3, 4, 5, 6, 7, 8, 9).traverseValidation(i -> condition(i% 2 == 0, List.single(i), i))); + } + + @Test + public void testTraverseValidationSemigroup() { + assertEquals(success(empty()), empty().traverseValidation(listSemigroup(), constant(fail(List.single(0))))); + assertEquals(fail(List.single(0)), single("zero").traverseValidation(listSemigroup(), constant(fail(List.single(0))))); + assertEquals(success(empty()), empty().traverseValidation(listSemigroup(), constant(success(0)))); + assertEquals(success(single(0)), single("zero").traverseValidation(listSemigroup(), constant(success(0)))); + + assertEquals(success(arraySeq(0, 2, 4, 6, 8)), arraySeq(0, 2, 4, 6, 8).traverseValidation(listSemigroup(),i -> condition(i% 2 == 0, List.single(i), i))); + assertEquals(fail(arrayList(1, 3, 5, 7, 9)), arraySeq(0, 1, 2, 3, 4, 5, 6, 7, 8, 9).traverseValidation(listSemigroup(),i -> condition(i% 2 == 0, List.single(i), i))); } } From 3bce5e0b885b87d94396d7f1cd6fb889958c0e8c Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Wed, 29 Jul 2020 14:17:37 -0400 Subject: [PATCH 113/173] Fixes #414. Add fj.data.Either.leftMap, rightMap. Also adds LeftProjection.traverseP1, RightProjection.valueE(String), RightProjection.orValue. --- core/src/main/java/fj/data/Either.java | 101 +++- core/src/test/java/fj/data/EitherTest.java | 653 +++++++++++++++++++++ 2 files changed, 739 insertions(+), 15 deletions(-) create mode 100644 core/src/test/java/fj/data/EitherTest.java diff --git a/core/src/main/java/fj/data/Either.java b/core/src/main/java/fj/data/Either.java index a9a746a9..c132ec49 100644 --- a/core/src/main/java/fj/data/Either.java +++ b/core/src/main/java/fj/data/Either.java @@ -1,27 +1,16 @@ package fj.data; -import fj.Equal; -import fj.F; -import fj.F0; -import fj.Function; -import fj.Hash; -import fj.P1; -import fj.Show; -import fj.Unit; +import fj.*; import fj.function.Effect1; -import java.util.Collection; -import java.util.Iterator; +import java.util.*; import static fj.Bottom.error; -import static fj.Function.compose; -import static fj.Function.identity; +import static fj.Function.*; import static fj.P.p; import static fj.Unit.unit; import static fj.data.Array.mkArray; -import static fj.data.List.cons_; -import static fj.data.List.list; -import static fj.data.List.single; +import static fj.data.List.*; import static fj.data.Option.some; /** @@ -321,6 +310,19 @@ public IO> traverseIO(final F> f) { IOFunctions.unit(Either.right(e.right().value())); } + /** + * Traverse this left with the given function and collect the output as a p1. + * + * @param f the given function + * @param the type of the p1 value + * @return the p1 + */ + public P1> traverseP1(final F> f) { + return e.isLeft() ? + f.f(value()).map(left_()) : + p(right(e.right().value())); + } + /** * Returns None if this projection has no value or if the given predicate * p does not hold for the value, otherwise, returns a right in Some. @@ -464,6 +466,16 @@ public Either either() { return e; } + /** + * Returns the value of this projection or fails with the given error message. + * + * @param err The error message to fail with. + * @return The value of this projection + */ + public B valueE(final String err) { + return valueE(p(err)); + } + /** * Returns the value of this projection or fails with the given error message. * @@ -487,6 +499,16 @@ public B value() { return valueE(p("right.value on Left")); } + /** + * The value of this projection or the given argument. + * + * @param a The value to return if this projection has no value. + * @return The value of this projection or the given argument. + */ + public B orValue(final B a) { + return e.isRight() ? value() : a; + } + /** * The value of this projection or the given argument. * @@ -586,12 +608,26 @@ public IO> traverseIO(final F> f) { IOFunctions.lazy(() -> left(e.left().value())); } + /** + * Traverse this right with the given function and collect the output as a p1. + * + * @param f the given function + * @param the type of the p1 value + * @return the p1 + */ public P1> traverseP1(final F> f) { return e.isRight() ? f.f(value()).map(right_()) : p(left(e.left().value())); } + /** + * Traverse this right with the given function and collect the output as an option. + * + * @param f the given function + * @param the type of the option value + * @return the option + */ public Option> traverseOption(final F> f) { return e.isRight() ? f.f(value()).map(right_()) : @@ -758,6 +794,22 @@ public static F, X> either_(final F left, final F the type of the function output + * @return the either + */ + public final Either leftMap(final F f) { + return left().map(f); + } + + /** + * Return a function that maps a given function across this either's left projection. + * + * @param the type of the right value + * @param the type of the left value + * @param the type of the function output * @return A function that maps another function across an either's left projection. */ public static F, F, Either>> leftMap_() { @@ -765,6 +817,22 @@ public static F, F, Either>> leftMap_() { } /** + * Map the given function across this either's right. + * + * @param f the given function + * @param the type of the function output + * @return the either + */ + public final Either rightMap(final F f) { + return right().map(f); + } + + /** + * Return a function that maps a given function across this either's right projection. + * + * @param the type of the right value + * @param the type of the left value + * @param the type of the function output * @return A function that maps another function across an either's right projection. */ public static F, F, Either>> rightMap_() { @@ -796,6 +864,7 @@ public static Either joinRight(final Either> e) { * * @param a The list of values to sequence with the either monad. * @return A sequenced value. + * @see fj.data.List#sequenceEitherLeft */ public static Either, X> sequenceLeft(final List> a) { return a.isEmpty() ? @@ -808,6 +877,8 @@ public static Either, X> sequenceLeft(final List> a) * * @param a The list of values to sequence with the either monad. * @return A sequenced value. + * @see fj.data.List#sequenceEither + * @see fj.data.List#sequenceEitherRight */ public static Either> sequenceRight(final List> a) { return a.isEmpty() ? diff --git a/core/src/test/java/fj/data/EitherTest.java b/core/src/test/java/fj/data/EitherTest.java new file mode 100644 index 00000000..31a83347 --- /dev/null +++ b/core/src/test/java/fj/data/EitherTest.java @@ -0,0 +1,653 @@ +package fj.data; + +import org.junit.Test; + +import java.io.IOException; + +import static fj.Function.constant; +import static fj.P.p; +import static fj.Unit.unit; +import static fj.data.Either.*; +import static org.junit.Assert.*; + +public final class EitherTest { + + public static final class LeftProjectionTest { + @Test + public void testIterator() { + assertEquals(0L, (long) left(0L).left().iterator().next()); + assertFalse(right(0).left().iterator().hasNext()); + } + + @Test + public void testEither() { + assertEquals(left(0), left(0).left().either()); + assertEquals(right(0), right(0).left().either()); + } + + @Test + public void testValueEString() { + assertEquals(0L, (long) left(0L).left().valueE("zero")); + + try { + right(0L).left().valueE("zero"); + fail(); + } catch (final Error error) { + assertEquals("zero", error.getMessage()); + } + } + + @Test + public void testValueEF0() { + assertEquals(0L, (long) left(0L).left().valueE(() -> "zero")); + + try { + right(0L).left().valueE(() -> "zero"); + fail(); + } catch (final Error error) { + assertEquals("zero", error.getMessage()); + } + } + + @Test + public void testValue() { + assertEquals(0L, (long) left(0L).left().value()); + + try { + right(0L).left().value(); + fail(); + } catch (final Error error) { + // pass + } + } + + @Test + public void testOrValue() { + assertEquals(0L, (long) left(0L).left().orValue(1L)); + assertEquals(1L, (long) right(0L).left().orValue(1L)); + } + + @Test + public void testOrValueF0() { + assertEquals(0L, (long) left(0L).left().orValue(() -> 1L)); + assertEquals(1L, (long) right(0L).left().orValue(() -> 1L)); + } + + @Test + public void testOn() { + assertEquals(0L, (long) Either.left(0L).left().on(constant(1L))); + assertEquals(1L, (long) Either.right(0L).left().on(constant(1L))); + } + + @Test + public void testForeach() { + left(0).left().foreach(constant(unit())); + right(0).left().foreach(ignore -> { + fail(); + return unit(); + }); + } + + @Test + public void testForeachDoEffect() { + left(0).left().foreachDoEffect(ignore -> { + }); + right(0).left().foreachDoEffect(ignore -> fail()); + } + + @Test + public void testMap() { + assertEquals(left(0), left("zero").left().map(constant(0))); + assertEquals(right("zero"), right("zero").left().map(constant(0))); + } + + @Test + public void testBind() { + assertEquals(left(0), left("zero").left().bind(constant(left(0)))); + assertEquals(right("zero"), right("zero").left().bind(constant(left(0)))); + } + + @Test + public void testSequence() { + assertEquals(left(0), left("zero").left().sequence(left(0))); + assertEquals(right(0), left("zero").left().sequence(right(0))); + assertEquals(right("zero"), right("zero").left().sequence(left(0))); + assertEquals(right("zero"), right("zero").left().sequence(right("one"))); + } + + @Test + public void testTraverseList() { + assertEquals(List.nil(), left("zero").left().traverseList(constant(List.nil()))); + assertEquals(List.single(left(0)), left("zero").left().traverseList(constant(List.single(0)))); + assertEquals(List.arrayList(left(0), left(1)), left("zero").left().traverseList(constant(List.arrayList(0, 1)))); + assertEquals(List.single(right("zero")), right("zero").left().traverseList(constant(List.nil()))); + assertEquals(List.single(right("zero")), right("zero").left().traverseList(constant(List.single(0)))); + assertEquals(List.single(right("zero")), right("zero").left().traverseList(constant(List.arrayList(0, 1)))); + } + + @Test + public void testTraverseIO() throws IOException { + assertEquals(left(0), left("zero").left().traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + assertEquals(right("zero"), right("zero").left().traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + } + + @Test + public void testTraverseP1() { + assertEquals(p(left(0)), left("zero").left().traverseP1(constant(p(0)))); + assertEquals(p(right("zero")), right("zero").left().traverseP1(constant(p(0)))); + } + + @Test + public void testFilter() { + assertEquals(Option.none(), left(0).left().filter(constant(false))); + assertEquals(Option.none(), right(0).left().filter(constant(false))); + assertEquals(Option.some(left(0)), left(0).left().filter(constant(true))); + assertEquals(Option.none(), right(0).left().filter(constant(true))); + } + + @Test + public void testApply() { + assertEquals(left(1), left("zero").left().apply(left(constant(1)))); + assertEquals(right("zero"), right("zero").left().apply(left(constant(1)))); + } + + @Test + public void testForAll() { + assertFalse(left(0).left().forall(constant(false))); + assertTrue(right(0).left().forall(constant(false))); + assertTrue(left(0).left().forall(constant(true))); + assertTrue(right(0).left().forall(constant(true))); + } + + @Test + public void testExists() { + assertFalse(left(0).left().exists(constant(false))); + assertFalse(right(0).left().exists(constant(false))); + assertTrue(left(0).left().exists(constant(true))); + assertFalse(right(0).left().exists(constant(true))); + } + + @Test + public void testToList() { + assertEquals(List.single(0), left(0).left().toList()); + assertEquals(List.nil(), right(0).left().toList()); + } + + @Test + public void testToOption() { + assertEquals(Option.some(0), left(0).left().toOption()); + assertEquals(Option.none(), right(0).left().toOption()); + } + + @Test + public void testToArray() { + assertEquals(Array.single(0), left(0).left().toArray()); + assertEquals(Array.empty(), right(0).left().toArray()); + } + + @Test + public void testToStream() { + assertEquals(Stream.single(0), left(0).left().toStream()); + assertEquals(Stream.nil(), right(0).left().toStream()); + } + + @Test + public void testToCollection() { + assertEquals(1L, left(0L).left().toCollection().size()); + assertEquals(0L, (long) left(0L).left().toCollection().iterator().next()); + assertTrue(right(0).left().toCollection().isEmpty()); + } + + @Test + public void testTraverseOption() { + assertEquals(Option.none(), left("zero").left().traverseOption(constant(Option.none()))); + assertEquals(Option.some(left(0)), left("zero").left().traverseOption(constant(Option.some(0)))); + assertEquals(Option.some(right("zero")), right("zero").left().traverseOption(constant(Option.none()))); + assertEquals(Option.some(right("zero")), right("zero").left().traverseOption(constant(Option.some(0)))); + } + + @Test + public void testTraverseStream() { + assertEquals(Stream.nil(), left("zero").left().traverseStream(constant(Stream.nil()))); + assertEquals(Stream.single(left(0)), left("zero").left().traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.arrayStream(left(0), left(1)), left("zero").left().traverseStream(constant(Stream.arrayStream(0, 1)))); + assertEquals(Stream.single(right("zero")), right("zero").left().traverseStream(constant(Stream.nil()))); + assertEquals(Stream.single(right("zero")), right("zero").left().traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.single(right("zero")), right("zero").left().traverseStream(constant(Stream.arrayStream(0, 1)))); + } + } + + public static final class RightProjectionTest { + @Test + public void testIterator() { + assertEquals(0L, (long) right(0L).right().iterator().next()); + assertFalse(left(0).right().iterator().hasNext()); + } + + @Test + public void testEither() { + assertEquals(right(0), right(0).right().either()); + assertEquals(left(0), left(0).right().either()); + } + + @Test + public void testValueEString() { + assertEquals(0L, (long) right(0L).right().valueE("zero")); + + try { + left(0L).right().valueE("zero"); + fail(); + } catch (final Error error) { + assertEquals("zero", error.getMessage()); + } + } + + @Test + public void testValueEF0() { + assertEquals(0L, (long) right(0L).right().valueE(() -> "zero")); + + try { + left(0L).right().valueE(() -> "zero"); + fail(); + } catch (final Error error) { + assertEquals("zero", error.getMessage()); + } + } + + @Test + public void testValue() { + assertEquals(0L, (long) right(0L).right().value()); + + try { + left(0L).right().value(); + fail(); + } catch (final Error error) { + // pass + } + } + + @Test + public void testOrValue() { + assertEquals(0L, (long) right(0L).right().orValue(1L)); + assertEquals(1L, (long) left(0L).right().orValue(1L)); + } + + @Test + public void testOrValueF0() { + assertEquals(0L, (long) right(0L).right().orValue(() -> 1L)); + assertEquals(1L, (long) left(0L).right().orValue(() -> 1L)); + } + + @Test + public void testOn() { + assertEquals(0L, (long) Either.right(0L).right().on(constant(1L))); + assertEquals(1L, (long) Either.left(0L).right().on(constant(1L))); + } + + @Test + public void testForeach() { + right(0).right().foreach(constant(unit())); + left(0).right().foreach(ignore -> { + fail(); + return unit(); + }); + } + + @Test + public void testForeachDoEffect() { + right(0).right().foreachDoEffect(ignore -> { + }); + left(0).right().foreachDoEffect(ignore -> fail()); + } + + @Test + public void testMap() { + assertEquals(right(0), right("zero").right().map(constant(0))); + assertEquals(left("zero"), left("zero").right().map(constant(0))); + } + + @Test + public void testBind() { + assertEquals(right(0), right("zero").right().bind(constant(right(0)))); + assertEquals(left("zero"), left("zero").right().bind(constant(right(0)))); + } + + @Test + public void testSequence() { + assertEquals(right(0), right("zero").right().sequence(right(0))); + assertEquals(left(0), right("zero").right().sequence(left(0))); + assertEquals(left("zero"), left("zero").right().sequence(right(0))); + assertEquals(left("zero"), left("zero").right().sequence(left("one"))); + } + + @Test + public void testTraverseList() { + assertEquals(List.nil(), right("zero").right().traverseList(constant(List.nil()))); + assertEquals(List.single(right(0)), right("zero").right().traverseList(constant(List.single(0)))); + assertEquals(List.arrayList(right(0), right(1)), right("zero").right().traverseList(constant(List.arrayList(0, 1)))); + assertEquals(List.single(left("zero")), left("zero").right().traverseList(constant(List.nil()))); + assertEquals(List.single(left("zero")), left("zero").right().traverseList(constant(List.single(0)))); + assertEquals(List.single(left("zero")), left("zero").right().traverseList(constant(List.arrayList(0, 1)))); + } + + @Test + public void testTraverseIO() throws IOException { + assertEquals(right(0), right("zero").right().traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + assertEquals(left("zero"), left("zero").right().traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + } + + @Test + public void testTraverseP1() { + assertEquals(p(right(0)), right("zero").right().traverseP1(constant(p(0)))); + assertEquals(p(left("zero")), left("zero").right().traverseP1(constant(p(0)))); + } + + @Test + public void testFilter() { + assertEquals(Option.none(), right(0).right().filter(constant(false))); + assertEquals(Option.none(), left(0).right().filter(constant(false))); + assertEquals(Option.some(right(0)), right(0).right().filter(constant(true))); + assertEquals(Option.none(), left(0).right().filter(constant(true))); + } + + @Test + public void testApply() { + assertEquals(right(1), right("zero").right().apply(right(constant(1)))); + assertEquals(left("zero"), left("zero").right().apply(right(constant(1)))); + } + + @Test + public void testForAll() { + assertFalse(right(0).right().forall(constant(false))); + assertTrue(left(0).right().forall(constant(false))); + assertTrue(right(0).right().forall(constant(true))); + assertTrue(left(0).right().forall(constant(true))); + } + + @Test + public void testExists() { + assertFalse(right(0).right().exists(constant(false))); + assertFalse(left(0).right().exists(constant(false))); + assertTrue(right(0).right().exists(constant(true))); + assertFalse(left(0).right().exists(constant(true))); + } + + @Test + public void testToList() { + assertEquals(List.single(0), right(0).right().toList()); + assertEquals(List.nil(), left(0).right().toList()); + } + + @Test + public void testToOption() { + assertEquals(Option.some(0), right(0).right().toOption()); + assertEquals(Option.none(), left(0).right().toOption()); + } + + @Test + public void testToArray() { + assertEquals(Array.single(0), right(0).right().toArray()); + assertEquals(Array.empty(), left(0).right().toArray()); + } + + @Test + public void testToStream() { + assertEquals(Stream.single(0), right(0).right().toStream()); + assertEquals(Stream.nil(), left(0).right().toStream()); + } + + @Test + public void testToCollection() { + assertEquals(1L, right(0L).right().toCollection().size()); + assertEquals(0L, (long) right(0L).right().toCollection().iterator().next()); + assertTrue(left(0).right().toCollection().isEmpty()); + } + + @Test + public void testTraverseOption() { + assertEquals(Option.none(), right("zero").right().traverseOption(constant(Option.none()))); + assertEquals(Option.some(right(0)), right("zero").right().traverseOption(constant(Option.some(0)))); + assertEquals(Option.some(left("zero")), left("zero").right().traverseOption(constant(Option.none()))); + assertEquals(Option.some(left("zero")), left("zero").right().traverseOption(constant(Option.some(0)))); + } + + @Test + public void testTraverseStream() { + assertEquals(Stream.nil(), right("zero").right().traverseStream(constant(Stream.nil()))); + assertEquals(Stream.single(right(0)), right("zero").right().traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.arrayStream(right(0), right(1)), right("zero").right().traverseStream(constant(Stream.arrayStream(0, 1)))); + assertEquals(Stream.single(left("zero")), left("zero").right().traverseStream(constant(Stream.nil()))); + assertEquals(Stream.single(left("zero")), left("zero").right().traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.single(left("zero")), left("zero").right().traverseStream(constant(Stream.arrayStream(0, 1)))); + } + } + + @Test + public void testIsLeft() { + assertTrue(left(0).isLeft()); + assertFalse(right(0).isLeft()); + } + + @Test + public void testIsRight() { + assertFalse(left(0).isRight()); + assertTrue(right(0).isRight()); + } + + @Test + public void testEither() { + assertEquals(-1L, (long) left("zero").either(constant(-1L), constant(1L))); + assertEquals(1L, (long) right("zero").either(constant(-1L), constant(1L))); + } + + @Test + public void testBimap() { + assertEquals(left(-1), left("zero").bimap(constant(-1), constant(1))); + assertEquals(right(1), right("zero").bimap(constant(-1), constant(1))); + } + + @Test + public void testTestEquals() { + assertNotEquals(null, left(0)); + assertNotEquals(new Object(), left(0)); + assertNotEquals(left(0), right(0)); + assertEquals(left(0), left(0)); + + assertNotEquals(null, right(0)); + assertNotEquals(new Object(), right(0)); + assertEquals(right(0), right(0)); + assertNotEquals(right(0), left(0)); + } + + @Test + public void testTestHashCode() { + assertEquals(left(0).hashCode(), left(0).hashCode()); + assertEquals(left(0).hashCode(), right(0).hashCode()); + assertEquals(right(0).hashCode(), left(0).hashCode()); + assertEquals(right(0).hashCode(), left(0).hashCode()); + } + + @Test + public void testSwap() { + assertEquals(right(0), left(0).swap()); + assertEquals(left(0), right(0).swap()); + } + + @Test + public void testLeft_() { + assertEquals(left(0), left_().f(0)); + } + + @Test + public void testRight_() { + assertEquals(right(0), right_().f(0)); + } + + @Test + public void testEither_() { + assertEquals(-1L, (long) either_(constant(-1L), constant(1L)).f(left("zero"))); + assertEquals(1L, (long) either_(constant(-1L), constant(1L)).f(right("zero"))); + } + + @Test + public void testLeftMap() { + assertEquals(left(0), left("zero").leftMap(constant(0))); + assertEquals(right("zero"), right("zero").leftMap(constant(0))); + } + + @Test + public void testLeftMap_() { + assertEquals(left(0), leftMap_().f(constant(0)).f(left("zero"))); + assertEquals(right("zero"), leftMap_().f(constant(0)).f(right("zero"))); + } + + @Test + public void testRightMap() { + assertEquals(left("zero"), left("zero").rightMap(constant(0))); + assertEquals(right(0), right("zero").rightMap(constant(0))); + } + + @Test + public void testRightMap_() { + assertEquals(left("zero"), rightMap_().f(constant(0)).f(left("zero"))); + assertEquals(right(0), rightMap_().f(constant(0)).f(right("zero"))); + } + + @Test + public void testJoinLeft() { + assertEquals(left(0), joinLeft(left(left(0)))); + assertEquals(right(0), joinLeft(left(right(0)))); + assertEquals(right(left(0)), joinLeft(right(left(0)))); + assertEquals(right(right(0)), joinLeft(right(right(0)))); + } + + @Test + public void testJoinRight() { + assertEquals(left(left(0)), joinRight(left(left(0)))); + assertEquals(left(right(0)), joinRight(left(right(0)))); + assertEquals(left(0), joinRight(right(left(0)))); + assertEquals(right(0), joinRight(right(right(0)))); + } + + @Test + public void testSequenceLeft() { + assertEquals(left(List.nil()), sequenceLeft(List.nil())); + assertEquals(left(List.single("zero")), sequenceLeft(List.single(left("zero")))); + assertEquals(right("zero"), sequenceLeft(List.single(right("zero")))); + } + + @Test + public void testSequenceRight() { + assertEquals(right(List.nil()), sequenceRight(List.nil())); + assertEquals(right(List.single("zero")), sequenceRight(List.single(right("zero")))); + assertEquals(left("zero"), sequenceRight(List.single(left("zero")))); + } + + @Test + public void testTraverseListRight() { + assertEquals(List.single(left("zero")), left("zero").traverseListRight(constant(List.nil()))); + assertEquals(List.single(left("zero")), left("zero").traverseListRight(constant(List.single(0)))); + assertEquals(List.single(left("zero")), left("zero").traverseListRight(constant(List.arrayList(0, 1)))); + assertEquals(List.nil(), right("zero").traverseListRight(constant(List.nil()))); + assertEquals(List.single(right(0)), right("zero").traverseListRight(constant(List.single(0)))); + assertEquals(List.arrayList(right(0), right(1)), right("zero").traverseListRight(constant(List.arrayList(0, 1)))); + } + + @Test + public void testTraverseListLeft() { + assertEquals(List.nil(), left("zero").traverseListLeft(constant(List.nil()))); + assertEquals(List.single(left(0)), left("zero").traverseListLeft(constant(List.single(0)))); + assertEquals(List.arrayList(left(0), left(1)), left("zero").traverseListLeft(constant(List.arrayList(0, 1)))); + assertEquals(List.single(right("zero")), right("zero").traverseListLeft(constant(List.nil()))); + assertEquals(List.single(right("zero")), right("zero").traverseListLeft(constant(List.single(0)))); + assertEquals(List.single(right("zero")), right("zero").traverseListLeft(constant(List.arrayList(0, 1)))); + } + + @Test + public void testTraverseIORight() throws IOException { + assertEquals(left("zero"), left("zero").traverseIORight(constant(IOFunctions.lazy(constant(0)))).run()); + assertEquals(right(0), right("zero").traverseIORight(constant(IOFunctions.lazy(constant(0)))).run()); + } + + @Test + public void testTraverseIOLeft() throws IOException { + assertEquals(left(0), left("zero").traverseIOLeft(constant(IOFunctions.lazy(constant(0)))).run()); + assertEquals(right("zero"), right("zero").traverseIOLeft(constant(IOFunctions.lazy(constant(0)))).run()); + } + + @Test + public void testTraverseOptionRight() { + assertEquals(Option.some(left("zero")), left("zero").traverseOptionRight(constant(Option.none()))); + assertEquals(Option.some(left("zero")), left("zero").traverseOptionRight(constant(Option.some(0)))); + assertEquals(Option.none(), right("zero").traverseOptionRight(constant(Option.none()))); + assertEquals(Option.some(right(0)), right("zero").traverseOptionRight(constant(Option.some(0)))); + } + + @Test + public void testTraverseOptionLeft() { + assertEquals(Option.none(), left("zero").traverseOptionLeft(constant(Option.none()))); + assertEquals(Option.some(left(0)), left("zero").traverseOptionLeft(constant(Option.some(0)))); + assertEquals(Option.some(right("zero")), right("zero").traverseOptionLeft(constant(Option.none()))); + assertEquals(Option.some(right("zero")), right("zero").traverseOptionLeft(constant(Option.some(0)))); + } + + @Test + public void testTraverseStreamRight() { + assertEquals(Stream.single(left("zero")), left("zero").traverseStreamRight(constant(Stream.nil()))); + assertEquals(Stream.single(left("zero")), left("zero").traverseStreamRight(constant(Stream.single(0)))); + assertEquals(Stream.single(left("zero")), left("zero").traverseStreamRight(constant(Stream.arrayStream(0, 1)))); + assertEquals(Stream.nil(), right("zero").traverseStreamRight(constant(Stream.nil()))); + assertEquals(Stream.single(right(0)), right("zero").traverseStreamRight(constant(Stream.single(0)))); + assertEquals(Stream.arrayStream(right(0), right(1)), right("zero").traverseStreamRight(constant(Stream.arrayStream(0, 1)))); + } + + @Test + public void testTraverseStreamLeft() { + assertEquals(Stream.nil(), left("zero").traverseStreamLeft(constant(Stream.nil()))); + assertEquals(Stream.single(left(0)), left("zero").traverseStreamLeft(constant(Stream.single(0)))); + assertEquals(Stream.arrayStream(left(0), left(1)), left("zero").traverseStreamLeft(constant(Stream.arrayStream(0, 1)))); + assertEquals(Stream.single(right("zero")), right("zero").traverseStreamLeft(constant(Stream.nil()))); + assertEquals(Stream.single(right("zero")), right("zero").traverseStreamLeft(constant(Stream.single(0)))); + assertEquals(Stream.single(right("zero")), right("zero").traverseStreamLeft(constant(Stream.arrayStream(0, 1)))); + } + + @Test + public void testReduce() { + assertEquals(0L, (long) reduce(left(0L))); + assertEquals(0L, (long) reduce(right(0L))); + } + + @Test + public void testIif() { + assertEquals(right(-1), iif(true, () -> -1, () -> 1)); + assertEquals(left(1), iif(false, () -> -1, () -> 1)); + } + + @Test + public void testLefts() { + assertEquals(List.nil(), lefts(List.nil())); + assertEquals(List.single(0), lefts(List.single(left(0)))); + assertEquals(List.nil(), lefts(List.single(right(0)))); + assertEquals(List.arrayList(0, 1), lefts(List.arrayList(left(0), left(1)))); + assertEquals(List.single(0), lefts(List.arrayList(left(0), right(1)))); + assertEquals(List.single(1), lefts(List.arrayList(right(0), left(1)))); + assertEquals(List.nil(), lefts(List.arrayList(right(0), right(1)))); + } + + @Test + public void testRights() { + assertEquals(List.nil(), rights(List.nil())); + assertEquals(List.single(0), rights(List.single(right(0)))); + assertEquals(List.single(0), lefts(List.single(left(0)))); + assertEquals(List.arrayList(0, 1), rights(List.arrayList(right(0), right(1)))); + assertEquals(List.single(0), rights(List.arrayList(right(0), left(1)))); + assertEquals(List.single(1), rights(List.arrayList(left(0), right(1)))); + assertEquals(List.nil(), rights(List.arrayList(left(0), left(1)))); + } + + @Test + public void testTestToString() { + assertNotNull(left(0).toString()); + assertNotNull(right(0).toString()); + } +} \ No newline at end of file From 849dc39c6d5e937f9cd650da8e5de5cbf2bf062d Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Thu, 6 Aug 2020 10:35:26 -0400 Subject: [PATCH 114/173] Fixes #417. Add fj.data.State.bind(F>). --- core/src/main/java/fj/data/State.java | 18 +++++++++++ core/src/test/java/fj/data/StateTest.java | 39 +++++++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/fj/data/State.java b/core/src/main/java/fj/data/State.java index f29d55d7..f0156b36 100644 --- a/core/src/main/java/fj/data/State.java +++ b/core/src/main/java/fj/data/State.java @@ -111,6 +111,24 @@ public State withs(F f) { return suspended(s -> runF.f(f.f(s))); } + /** + * Bind the given function across this state. + * + * @param f the given function + * @param the type of the output value + * @return the state + */ + public State bind(F> f) { + return flatMap(f); + } + + /** + * Bind the given function across this state. + * + * @param f the given function + * @param the type of the output value + * @return the state + */ public State flatMap(F> f) { return suspended(s -> runF.f(s).bind(result -> Trampoline.pure(f.f(result._2()).run(result._1())))); } diff --git a/core/src/test/java/fj/data/StateTest.java b/core/src/test/java/fj/data/StateTest.java index 9fc8a9fb..37b21030 100644 --- a/core/src/test/java/fj/data/StateTest.java +++ b/core/src/test/java/fj/data/StateTest.java @@ -2,14 +2,47 @@ import org.junit.Test; +import static fj.P.p; +import static org.junit.Assert.assertEquals; + /** * Created by MarkPerry on 18/12/2014. */ public class StateTest { - @Test - public void map() { + @Test + public void testBind() { + assertEquals(p(2, "one"), state().run(1)); + assertEquals(p(3, "two"), state().run(2)); + assertEquals(p(4, "three"), state().run(3)); + assertEquals(p(2, "?"), state().bind(state -> State.constant("?")).run(1)); + assertEquals(p(3, "?"), state().bind(state -> State.constant("?")).run(2)); + assertEquals(p(4, "?"), state().bind(state -> State.constant("?")).run(3)); + } + + @Test + public void testFlatMap() { + assertEquals(p(2, "one"), state().run(1)); + assertEquals(p(3, "two"), state().run(2)); + assertEquals(p(4, "three"), state().run(3)); + assertEquals(p(2, "?"), state().flatMap(state -> State.constant("?")).run(1)); + assertEquals(p(3, "?"), state().flatMap(state -> State.constant("?")).run(2)); + assertEquals(p(4, "?"), state().flatMap(state -> State.constant("?")).run(3)); + } - } + private static final State state() { + return State.unit(i -> p(i + 1, toLapine(i))); + } + private static String toLapine( + final int i) { + return i == 1 ? + "one" : + i == 2 ? + "two" : + i == 3 ? + "three" : + i == 4 ? + "four" : "hrair"; + } } From c427a086d6a06a14074719d2f3c9a011ce7ad14d Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Thu, 22 Oct 2020 07:07:30 -0400 Subject: [PATCH 115/173] Fixes #424. Add fj.data.Stream.sequence*, fj.data.Stream.traverse* functions. --- core/src/main/java/fj/data/Stream.java | 432 +++++++++++++++++- .../test/java/fj/data/IOFunctionsTest.java | 2 +- core/src/test/java/fj/data/StreamTest.java | 237 ++++++++++ 3 files changed, 649 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/fj/data/Stream.java b/core/src/main/java/fj/data/Stream.java index a523515b..e05e26bf 100644 --- a/core/src/main/java/fj/data/Stream.java +++ b/core/src/main/java/fj/data/Stream.java @@ -4,6 +4,7 @@ import fj.F0; import fj.F3; import fj.Hash; +import fj.Semigroup; import fj.Show; import fj.F; import fj.F2; @@ -14,6 +15,7 @@ import fj.P1; import fj.P2; import fj.Unit; +import fj.control.Trampoline; import fj.control.parallel.Promise; import fj.control.parallel.Strategy; import fj.Ordering; @@ -1094,27 +1096,6 @@ public final Stream takeWhile(final F f) { Stream.nil(); } - /** - * Traversable instance of Stream for IO. - * - * @return traversed value - */ - public final IO> traverseIO(F> f) { - return this.foldRight1((a, acc) -> - IOFunctions.bind(acc, (Stream bs) -> - IOFunctions.map(f.f(a), bs::cons)), IOFunctions.unit(Stream.nil())); - - } - - /** - * Traversable instance of Stream for Option. - * - * @return traversed value - */ - public final Option> traverseOption(F> f) { - return this.foldRight1((a, acc) -> acc.bind(bs -> f.f(a).map(bs::cons)), some(Stream.nil())); - } - /** * Removes elements from the head of this stream that do not match the given predicate function * until an element is found that does match or the stream is exhausted. @@ -1669,4 +1650,413 @@ public static F>, F, Stream>> bind_() { public static F, B>>, F, B>>> foldRight() { return curry((f, b, as) -> as.foldRight(f, b)); } + + /** + * Sequence the given stream and collect the output on the right side of an either. + * + * @param stream the given stream + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static Either> sequenceEither( + final Stream> stream) { + return stream.traverseEither(identity()); + } + + /** + * Sequence the given stream and collect the output on the left side of an either. + * + * @param stream the given stream + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static Either, R> sequenceEitherLeft( + final Stream> stream) { + return stream.traverseEitherLeft(identity()); + } + + /** + * Sequence the given stream and collect the output on the right side of an either. + * + * @param stream the given stream + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static Either> sequenceEitherRight( + final Stream> stream) { + return stream.traverseEitherRight(identity()); + } + + /** + * Sequence the given stream and collect the output as a function. + * + * @param stream the given stream + * @param the type of the input value + * @param the type of the output value + * @return the either + */ + public static F> sequenceF( + final Stream> stream) { + return stream.traverseF(identity()); + } + + /** + * Sequence the given stream and collect the output as an IO. + * + * @param stream the given stream + * @param the type of the IO value + * @return the IO + */ + public static IO> sequenceIO( + final Stream> stream) { + return stream.traverseIO(identity()); + } + + /** + * Sequence the given stream and collect the output as a list. + * + * @param stream the given stream + * @param the type of the list value + * @return the list + */ + public static List> sequenceList( + final Stream> stream) { + return stream.traverseList(identity()); + } + + /** + * Sequence the given stream and collect the output as an stream. + * + * @param stream the given stream + * @param the type of the option value + * @return the stream + */ + public static Option> sequenceOption( + final Stream> stream) { + return stream.traverseOption(identity()); + } + + /** + * Sequence the given stream and collect the output as a P1. + * + * @param stream the given stream + * @param the type of the P1 value + * @return the P1 + */ + public static P1> sequenceP1( + final Stream> stream) { + return stream.traverseP1(identity()); + } + + /** + * Sequence the given stream and collect the output as a seq. + * + * @param stream the given stream + * @param the type of the stream value + * @return the seq + */ + public static Seq> sequenceSeq( + final Stream> stream) { + return stream.traverseSeq(identity()); + } + + /** + * Sequence the given stream and collect the output as a set; use the given ord to order the set. + * + * @param ord the given ord + * @param stream the given stream + * @param the type of the set value + * @return the either + */ + public static Set> sequenceSet( + final Ord ord, + final Stream> stream) { + return stream.traverseSet(ord, identity()); + } + + /** + * Sequence the given stream and collect the output as a stream. + * + * @param stream the given stream + * @param the type of the stream value + * @return the stream + */ + public static Stream> sequenceStream( + final Stream> stream) { + return stream.traverseStream(identity()); + } + + /** + * Sequence the given stream and collect the output as a trampoline. + * + * @param stream the given trampoline + * @param the type of the stream value + * @return the stream + */ + public static Trampoline> sequenceTrampoline( + final Stream> stream) { + return stream.traverseTrampoline(identity()); + } + + /** + * Sequence the given stream and collect the output as a validation. + * + * @param stream the given stream + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public static Validation> sequenceValidation( + final Stream> stream) { + return stream.traverseValidation(identity()); + } + + /** + * Sequence the given stream and collect the output as a validation; use the given semigroup to reduce the errors. + * + * @param semigroup the given semigroup + * @param stream the given stream + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public static Validation> sequenceValidation( + final Semigroup semigroup, + final Stream> stream) { + return stream.traverseValidation(semigroup, identity()); + } + + /** + * Traverse this stream with the given function and collect the output on the right side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public Either> traverseEither( + final F> f) { + return traverseEitherRight(f); + } + + /** + * Traverse this stream with the given function and collect the output on the left side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public Either, R> traverseEitherLeft( + final F> f) { + return foldRight1( + ( + element, + either) -> f.f(element).left().bind(elementInner -> either.left().map(stream -> stream.cons(elementInner))), + Either.left(nil())); + } + + /** + * Traverse this stream with the given function and collect the output on the right side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public Either> traverseEitherRight( + final F> f) { + return foldRight1( + ( + element, + either) -> f.f(element).right().bind(elementInner -> either.right().map(stream -> stream.cons(elementInner))), + Either.right(nil())); + } + + /** + * Traverse this stream with the given function and collect the output as a function. + * + * @param f the given function + * @param the type of the input value + * @param the type of the output value + * @return the function + */ + public F> traverseF( + final F> f) { + return foldRight1( + ( + element, + fInner) -> Function.bind(f.f(element), elementInner -> andThen(fInner, stream -> stream.cons(elementInner))), + Function.constant(nil())); + } + + /** + * Traverse this stream with the given function and collect the output as an IO. + * + * @param f the given function + * @param the type of the IO value + * @return the IO + */ + public IO> traverseIO( + final F> f) { + return foldRight1( + ( + element, + io) -> IOFunctions.bind(f.f(element), elementInner -> IOFunctions.map(io, stream -> stream.cons(elementInner))), + IOFunctions.unit(nil())); + } + + /** + * Traverse this stream with the given function and collect the output as a list. + * + * @param f the given function + * @param the type of the list value + * @return the list + */ + public List> traverseList( + final F> f) { + return foldRight1( + ( + element, + list) -> f.f(element).bind(elementInner -> list.map(stream -> stream.cons(elementInner))), + List.single(nil())); + } + + /** + * Traverses through the Seq with the given function + * + * @param f The function that produces Option value + * @return none if applying f returns none to any element of the seq or f mapped seq in some . + */ + public Option> traverseOption( + final F> f) { + return foldRight1( + ( + element, + option) -> f.f(element).bind(elementInner -> option.map(stream -> stream.cons(elementInner))), + Option.some(nil())); + } + + /** + * Traverse this stream with the given function and collect the output as a p1. + * + * @param f the given function + * @param the type of the p1 value + * @return the p1 + */ + public P1> traverseP1( + final F> f) { + return foldRight1( + ( + element, + p1) -> f.f(element).bind(elementInner -> p1.map(stream -> stream.cons(elementInner))), + P.p(nil())); + } + + /** + * Traverse this stream with the given function and collect the output as a seq. + * + * @param f the given function + * @param the type of the seq value + * @return the seq + */ + public Seq> traverseSeq( + final F> f) { + return foldRight1( + ( + element, + seq) -> f.f(element).bind(elementInner -> seq.map(stream -> stream.cons(elementInner))), + Seq.single(nil())); + } + + /** + * Traverse this stream with the given function and collect the output as a set; use the given ord to order the set. + * + * @param ord the given ord + * @param f the given function + * @param the type of the set value + * @return the set + */ + public Set> traverseSet( + final Ord ord, + final F> f) { + final Ord> seqOrd = Ord.streamOrd(ord); + return foldRight1( + ( + element, + set) -> f.f(element).bind(seqOrd, elementInner -> set.map(seqOrd, seq -> seq.cons(elementInner))), + Set.single(seqOrd, nil())); + } + + /** + * Traverse this stream with the given function and collect the output as a stream. + * + * @param f the given function + * @param the type of the stream value + * @return the stream + */ + public Stream> traverseStream( + final F> f) { + return foldRight1( + ( + element, + stream) -> f.f(element).bind(elementInner -> stream.map(seq -> seq.cons(elementInner))), + Stream.single(nil())); + } + + /** + * Traverse this stream with the given function and collect the output as a trampoline. + * + * @param f the given function + * @param the type of the trampoline value + * @return the trampoline + */ + public Trampoline> traverseTrampoline( + final F> f) { + return foldRight1( + ( + element, + trampoline) -> f.f(element).bind(elementInner -> trampoline.map(seq -> seq.cons(elementInner))), + Trampoline.pure(nil())); + } + + /** + * Traverse this stream with the given function and collect the output as a validation. + * + * @param f the given function + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public final Validation> traverseValidation( + final F> f) { + return foldRight1( + ( + element, + validation) -> f.f(element).bind(elementInner -> validation.map(stream -> stream.cons(elementInner))), + Validation.success(nil())); + } + + /** + * Traverse this stream with the given function and collect the output as a validation; use the given semigroup to + * reduce the errors. + * + * @param semigroup the given semigroup + * @param f the given function + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public final Validation> traverseValidation( + final Semigroup semigroup, + final F> f) { + return foldRight1( + ( + element, + validation) -> f.f(element).map(Stream::single).accumulate(semigroup, validation, (stream1, stream2) -> stream1.append(stream2)), + Validation.success(nil())); + } } diff --git a/core/src/test/java/fj/data/IOFunctionsTest.java b/core/src/test/java/fj/data/IOFunctionsTest.java index b943db36..5213878c 100644 --- a/core/src/test/java/fj/data/IOFunctionsTest.java +++ b/core/src/test/java/fj/data/IOFunctionsTest.java @@ -73,7 +73,7 @@ public void testTraverseIO() throws IOException { System.setOut(new PrintStream(outContent)); stream.traverseIO(IOFunctions::stdoutPrint).run(); System.setOut(originalOut); - assertThat(outContent.toString(), is("foobar3bar2foo1")); + assertThat(outContent.toString(), is("foo1bar2foobar3")); } @Test diff --git a/core/src/test/java/fj/data/StreamTest.java b/core/src/test/java/fj/data/StreamTest.java index 999a3a3e..313678aa 100644 --- a/core/src/test/java/fj/data/StreamTest.java +++ b/core/src/test/java/fj/data/StreamTest.java @@ -2,13 +2,40 @@ import fj.Equal; import fj.P2; +import fj.control.Trampoline; import org.junit.Test; +import java.io.IOException; import java.util.ConcurrentModificationException; +import static fj.Function.constant; +import static fj.Ord.*; +import static fj.P.p; +import static fj.Semigroup.listSemigroup; +import static fj.data.Either.left; +import static fj.data.Either.right; +import static fj.data.List.arrayList; +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.data.Seq.arraySeq; +import static fj.data.Seq.empty; +import static fj.data.Stream.sequenceEitherLeft; +import static fj.data.Stream.sequenceEitherRight; +import static fj.data.Stream.sequenceF; +import static fj.data.Stream.sequenceIO; +import static fj.data.Stream.sequenceList; +import static fj.data.Stream.sequenceOption; +import static fj.data.Stream.sequenceP1; +import static fj.data.Stream.sequenceSeq; +import static fj.data.Stream.sequenceSet; +import static fj.data.Stream.sequenceStream; +import static fj.data.Stream.sequenceTrampoline; +import static fj.data.Stream.sequenceValidation; import static fj.data.Stream.*; +import static fj.data.Validation.*; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; /** @@ -103,4 +130,214 @@ public void testMinus() { assertThat(s1.minus(Equal.charEqual, s2), is(stream(new Character[]{'a', 'c', 'd'}))); } + + @Test + public void testSequenceEither() { + assertEquals(right(nil()), sequenceEither(nil())); + assertEquals(right(single("zero")), sequenceEither(single(right("zero")))); + assertEquals(left("zero"), sequenceEither(single(left("zero")))); + } + + @Test + public void testSequenceEitherLeft() { + assertEquals(left(nil()), sequenceEitherLeft(nil())); + assertEquals(left(single("zero")), sequenceEitherLeft(single(left("zero")))); + assertEquals(right("zero"), sequenceEitherLeft(single(right("zero")))); + } + + @Test + public void testSequenceEitherRight() { + assertEquals(right(nil()), sequenceEitherRight(nil())); + assertEquals(right(single("zero")), sequenceEitherRight(single(right("zero")))); + assertEquals(left("zero"), sequenceEitherRight(single(left("zero")))); + } + + @Test + public void testSequenceF() { + assertEquals(constant(nil()).f(1), sequenceF(nil()).f(1)); + assertEquals(constant(single("zero")).f(1), sequenceF(single(constant("zero"))).f(1)); + } + + @Test + public void testSequenceIO() + throws IOException { + assertEquals(IOFunctions.lazy(constant(nil())).run(), sequenceIO(nil()).run()); + assertEquals(IOFunctions.lazy(constant(single("zero"))).run(), sequenceIO(single(IOFunctions.lazy(constant("zero")))).run()); + } + + @Test + public void testSequenceList() { + assertEquals(List.single(nil()), sequenceList(nil())); + assertEquals(List.nil(), sequenceList(single(List.nil()))); + assertEquals(List.single(single("zero")), sequenceList(single(List.single("zero")))); + assertEquals(arrayList(single("zero"), single("one")), sequenceList(single(arrayList("zero", "one")))); + } + + @Test + public void testSequenceOption() { + assertEquals(some(nil()), sequenceOption(nil())); + assertEquals(none(), sequenceOption(single(none()))); + assertEquals(some(single("zero")), sequenceOption(single(some("zero")))); + } + + @Test + public void testSequenceP1() { + assertEquals(p(nil()), sequenceP1(nil())); + assertEquals(p(single("zero")), sequenceP1(single(p("zero")))); + } + + @Test + public void testSequenceSeq() { + assertEquals(Seq.single(nil()), sequenceSeq(nil())); + assertEquals(Seq.empty(), sequenceSeq(single(Seq.empty()))); + assertEquals(Seq.single(single("zero")), sequenceSeq(single(Seq.single("zero")))); + assertEquals(arraySeq(single("zero"), single("one")), sequenceSeq(single(arraySeq("zero", "one")))); + } + + @Test + public void testSequenceSet() { + assertEquals(Set.arraySet(streamOrd(stringOrd), nil()), sequenceSet(stringOrd, nil())); + assertEquals(Set.empty(streamOrd(stringOrd)), sequenceSet(stringOrd, single(Set.empty(stringOrd)))); + assertEquals(Set.arraySet(streamOrd(stringOrd), single("zero")), sequenceSet(stringOrd, single(Set.single(stringOrd, "zero")))); + assertEquals(Set.arraySet(streamOrd(stringOrd), single("zero"), single("one")), sequenceSet(stringOrd, single(Set.arraySet(stringOrd, "zero", "one")))); + } + + @Test + public void testSequenceStream() { + assertEquals(single(nil()), sequenceStream(nil())); + assertEquals(nil(), sequenceStream(single(nil()))); + assertEquals(single(single("zero")), sequenceStream(single(single("zero")))); + assertEquals(arrayStream(single("zero"), single("one")), sequenceStream(single(arrayStream("zero", "one")))); + } + + @Test + public void testSequenceTrampoline() { + assertEquals(Trampoline.pure(nil()).run(), sequenceTrampoline(nil()).run()); + assertEquals(Trampoline.pure(single(0)).run(), sequenceTrampoline(single(Trampoline.pure(0))).run()); + } + + @Test + public void testSequenceValidation() { + assertEquals(success(nil()), sequenceValidation(nil())); + assertEquals(fail(single(0)), sequenceValidation(single(fail(single(0))))); + assertEquals(success(single(0)), sequenceValidation(single(success(0)))); + } + + @Test + public void testSequenceValidationSemigroup() { + assertEquals(success(nil()), sequenceValidation(listSemigroup(), nil())); + assertEquals(fail(List.single(0)), sequenceValidation(listSemigroup(), single(fail(List.single(0))))); + assertEquals(success(single(0)), sequenceValidation(listSemigroup(), single(success(0)))); + } + + @Test + public void testTraverseEitherLeft() { + assertEquals(left(nil()), nil().traverseEitherLeft(constant(left(0)))); + assertEquals(left(single(0)), single("zero").traverseEitherLeft(constant(left(0)))); + assertEquals(left(nil()), nil().traverseEitherLeft(constant(right(0)))); + assertEquals(right(0), single("zero").traverseEitherLeft(constant(right(0)))); + } + + @Test + public void testTraverseEitherRight() { + assertEquals(right(nil()), nil().traverseEitherRight(constant(right(0)))); + assertEquals(right(single(0)), single("zero").traverseEitherRight(constant(right(0)))); + assertEquals(right(nil()), nil().traverseEitherRight(constant(left(0)))); + assertEquals(left(0), single("zero").traverseEitherRight(constant(left(0)))); + } + + @Test + public void testTraverseF() { + assertEquals(constant(nil()).f(1), nil().traverseF(constant(constant(0))).f(1)); + assertEquals(constant(single(0)).f(1), single("zero").traverseF(constant(constant(0))).f(1)); + } + + @Test + public void testTraverseIO() + throws IOException { + assertEquals(IOFunctions.lazy(constant(nil())).run(), nil().traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + assertEquals(IOFunctions.lazy(constant(single(0))).run(), single("zero").traverseIO(constant(IOFunctions.lazy(constant(0)))).run()); + } + + @Test + public void testTraverseList() { + assertEquals(List.single(nil()), nil().traverseList(constant(List.nil()))); + assertEquals(List.nil(), single("zero").traverseList(constant(List.nil()))); + assertEquals(List.single(nil()), nil().traverseList(constant(List.single(0)))); + assertEquals(List.single(single(0)), single("zero").traverseList(constant(List.single(0)))); + assertEquals(List.single(nil()), nil().traverseList(constant(arrayList(0, 1)))); + assertEquals(arrayList(single(0), single(1)), single("zero").traverseList(constant(arrayList(0, 1)))); + } + + @Test + public void testTraverseOption() { + assertEquals(some(nil()), nil().traverseOption(constant(none()))); + assertEquals(none(), single("zero").traverseOption(constant(none()))); + assertEquals(some(nil()), nil().traverseOption(constant(some(0)))); + assertEquals(some(single(0)), single("zero").traverseOption(constant(some(0)))); + } + + @Test + public void testTraverseP1() { + assertEquals(p(nil()), nil().traverseP1(constant(p(0)))); + assertEquals(p(single(0)), single("zero").traverseP1(constant(p(0)))); + } + + @Test + public void testTraverseSeq() { + assertEquals(Seq.single(nil()), nil().traverseSeq(constant(empty()))); + assertEquals(Seq.empty(), single("zero").traverseSeq(constant(empty()))); + assertEquals(Seq.single(nil()), nil().traverseSeq(constant(Seq.single(0)))); + assertEquals(Seq.single(single(0)), single("zero").traverseSeq(constant(Seq.single(0)))); + assertEquals(Seq.single(nil()), nil().traverseSeq(constant(arraySeq(0, 1)))); + assertEquals(arraySeq(single(0), single(1)), single("zero").traverseSeq(constant(arraySeq(0, 1)))); + } + + @Test + public void testTraverseSet() { + assertEquals(Set.arraySet(streamOrd(intOrd), nil()), nil().traverseSet(intOrd, constant(Set.empty(intOrd)))); + assertEquals(Set.empty(streamOrd(intOrd)), single("zero").traverseSet(intOrd, constant(Set.empty(intOrd)))); + assertEquals(Set.single(streamOrd(intOrd), nil()), nil().traverseSet(intOrd, constant(Set.single(intOrd, 0)))); + assertEquals(Set.single(streamOrd(intOrd), single(0)), single("zero").traverseSet(intOrd, constant(Set.single(intOrd, 0)))); + assertEquals(Set.single(streamOrd(intOrd), nil()), nil().traverseSet(intOrd, constant(Set.arraySet(intOrd, 0, 1)))); + assertEquals(Set.arraySet(streamOrd(intOrd), single(0), single(1)), single("zero").traverseSet(intOrd, constant(Set.arraySet(intOrd, 0, 1)))); + } + + @Test + public void testTraverseStream() { + assertEquals(Stream.single(nil()), nil().traverseStream(constant(Stream.nil()))); + assertEquals(Stream.nil(), single("zero").traverseStream(constant(Stream.nil()))); + assertEquals(Stream.single(nil()), nil().traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.single(single(0)), single("zero").traverseStream(constant(Stream.single(0)))); + assertEquals(Stream.single(nil()), nil().traverseStream(constant(Stream.arrayStream(0, 1)))); + assertEquals(Stream.arrayStream(single(0), single(1)), single("zero").traverseStream(constant(Stream.arrayStream(0, 1)))); + } + + @Test + public void testTraverseTrampoline() { + assertEquals(Trampoline.pure(nil()).run(), nil().traverseTrampoline(constant(Trampoline.pure(0))).run()); + assertEquals(Trampoline.pure(single(0)).run(), single("zero").traverseTrampoline(constant(Trampoline.pure(0))).run()); + } + + @Test + public void testTraverseValidation() { + assertEquals(success(nil()), nil().traverseValidation(constant(fail(single(0))))); + assertEquals(fail(single(0)), single("zero").traverseValidation(constant(fail(single(0))))); + assertEquals(success(nil()), nil().traverseValidation(constant(success(0)))); + assertEquals(success(single(0)), single("zero").traverseValidation(constant(success(0)))); + + assertEquals(success(arraySeq(0, 2, 4, 6, 8)), arraySeq(0, 2, 4, 6, 8).traverseValidation(i -> condition(i % 2 == 0, List.single(i), i))); + assertEquals(fail(List.single(1)), arraySeq(0, 1, 2, 3, 4, 5, 6, 7, 8, 9).traverseValidation(i -> condition(i % 2 == 0, List.single(i), i))); + } + + @Test + public void testTraverseValidationSemigroup() { + assertEquals(success(nil()), nil().traverseValidation(listSemigroup(), constant(fail(List.single(0))))); + assertEquals(fail(List.single(0)), single("zero").traverseValidation(listSemigroup(), constant(fail(List.single(0))))); + assertEquals(success(nil()), nil().traverseValidation(listSemigroup(), constant(success(0)))); + assertEquals(success(single(0)), single("zero").traverseValidation(listSemigroup(), constant(success(0)))); + + assertEquals(success(arraySeq(0, 2, 4, 6, 8)), arraySeq(0, 2, 4, 6, 8).traverseValidation(listSemigroup(), i -> condition(i % 2 == 0, List.single(i), i))); + assertEquals(fail(arrayList(1, 3, 5, 7, 9)), arraySeq(0, 1, 2, 3, 4, 5, 6, 7, 8, 9).traverseValidation(listSemigroup(), i -> condition(i % 2 == 0, List.single(i), i))); + } } From 254e3f55d964ef27278dbfb51ef0981f7e07c96f Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Sat, 13 Feb 2021 14:24:27 +1000 Subject: [PATCH 116/173] Fixed code to not use Java 8 features --- .../fj/control/parallel/StrategyTest.java | 9 +++++- core/src/test/java/fj/data/OptionTest.java | 30 ++++++++--------- .../src/test/java/fj/data/ValidationTest.java | 32 +++++++++---------- 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/core/src/test/java/fj/control/parallel/StrategyTest.java b/core/src/test/java/fj/control/parallel/StrategyTest.java index 8475b35a..0c605829 100644 --- a/core/src/test/java/fj/control/parallel/StrategyTest.java +++ b/core/src/test/java/fj/control/parallel/StrategyTest.java @@ -5,6 +5,7 @@ import fj.P1; import fj.Unit; import fj.data.Enumerator; +import fj.data.Java; import fj.data.List; import fj.data.Stream; import org.junit.Test; @@ -49,10 +50,16 @@ public void testStrategyCompletion() { @Test public void testStrategyMergeAll() { final List l = List.range(0, 100); - final List> p1s = mergeAll(l.map(x -> CompletableFuture.supplyAsync(() -> x))); + final List> p1s = mergeAll(l.map(x -> future(x))); assertThat(P1.sequence(p1s)._1(), is(l)); } + public static Future future(A a) { + FutureTask ft = new FutureTask<>(() -> a); + new Thread(ft).start(); + return ft; + } + @Test public void testStrategyCallables() throws Exception { final Strategy> s = strategy(c -> c); diff --git a/core/src/test/java/fj/data/OptionTest.java b/core/src/test/java/fj/data/OptionTest.java index da5ef065..5a7bbdbf 100644 --- a/core/src/test/java/fj/data/OptionTest.java +++ b/core/src/test/java/fj/data/OptionTest.java @@ -75,7 +75,7 @@ public void testBind1() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), 0), list.index(0).bind(Option::some)); }); @@ -87,7 +87,7 @@ public void testBind2() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1)), list.index(0).bind(list.index(1), p2())); }); } @@ -98,7 +98,7 @@ public void testBind3() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2)), list.index(0).bind(list.index(1), list.index(2), p3())); }); @@ -110,7 +110,7 @@ public void testBind4() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3)), list.index(0).bind(list.index(1), list.index(2), list.index(3), p4())); }); @@ -122,7 +122,7 @@ public void testBind5() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4)), list.index(0).bind(list.index(1), list.index(2), list.index(3), list.index(4), p5())); }); } @@ -133,7 +133,7 @@ public void testBind6() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4, 5)), list.index(0).bind(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), p6())); }); } @@ -144,7 +144,7 @@ public void testBind7() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4, 5, 6)), list.index(0).bind(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), p7())); }); } @@ -155,7 +155,7 @@ public void testBind8() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4, 5, 6, 7)), list.index(0).bind(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), list.index(7), P.p8())); }); } @@ -166,7 +166,7 @@ public void testBindProduct2() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1)), list.index(0).bindProduct(list.index(1))); }); } @@ -177,7 +177,7 @@ public void testBindProduct3() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2)), list.index(0).bindProduct(list.index(1), list.index(2))); }); @@ -189,7 +189,7 @@ public void testBindProduct4() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3)), list.index(0).bindProduct(list.index(1), list.index(2), list.index(3))); }); @@ -201,7 +201,7 @@ public void testBindProduct5() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4)), list.index(0).bindProduct(list.index(1), list.index(2), list.index(3), list.index(4))); }); } @@ -212,7 +212,7 @@ public void testBindProduct6() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4, 5)), list.index(0).bindProduct(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5))); }); } @@ -223,7 +223,7 @@ public void testBindProduct7() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4, 5, 6)), list.index(0).bindProduct(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6))); }); } @@ -234,7 +234,7 @@ public void testBindProduct8() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List.>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.forall(Option::isSome), p(0, 1, 2, 3, 4, 5, 6, 7)), list.index(0).bindProduct(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), list.index(7))); }); } diff --git a/core/src/test/java/fj/data/ValidationTest.java b/core/src/test/java/fj/data/ValidationTest.java index 0df2deb9..bf2c34e9 100644 --- a/core/src/test/java/fj/data/ValidationTest.java +++ b/core/src/test/java/fj/data/ValidationTest.java @@ -35,7 +35,7 @@ public void testAccumulateSemigroup2() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(), list.index(1))); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1)), list.index(0).accumulate(Semigroup.listSemigroup(), list.index(1), p2())); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1)), list.index(0).accumulate(Semigroup.listSemigroup(), list.index(1), uncurryF2(p2()))); @@ -49,7 +49,7 @@ public void testAccumulateSemigroup3() { accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2))); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), p3())); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), uncurryF3(p3()))); @@ -64,7 +64,7 @@ public void testAccumulateSemigroup4() { accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3))); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), p4())); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), uncurryF4(p4()))); @@ -79,7 +79,7 @@ public void testAccumulateSemigroup5() { accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4))); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), p5())); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), uncurryF5(p5()))); @@ -93,7 +93,7 @@ public void testAccumulateSemigroup6() { accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5))); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), p6())); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), uncurryF6(p6()))); @@ -107,7 +107,7 @@ public void testAccumulateSemigroup7() { accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6))); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5, 6)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), p7())); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5, 6)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), uncurryF7(p7()))); @@ -121,7 +121,7 @@ public void testAccumulateSemigroup8() { accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(iif(list.exists(Validation::isFail), list.filter(Validation::isFail).bind(validation -> validation.fail())), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), list.index(7))); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5, 6, 7)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), list.index(7), P.p8())); assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).bind(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5, 6, 7)), list.index(0).accumulate(Semigroup.listSemigroup(),list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), list.index(7), uncurryF8(P.p8()))); @@ -134,7 +134,7 @@ public void testAccumulate0() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), 0), list.index(0).accumulate()); }); } @@ -145,7 +145,7 @@ public void testAccumulate1() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), 0), list.index(0).accumulate(identity())); }); @@ -157,7 +157,7 @@ public void testAccumulate2() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1)), list.index(0).accumulate(list.index(1), P::p)); }); } @@ -168,7 +168,7 @@ public void testAccumulate3() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1, 2)), list.index(0).accumulate(list.index(1), list.index(2), P::p)); }); @@ -180,7 +180,7 @@ public void testAccumulate4() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1, 2, 3)), list.index(0).accumulate(list.index(1), list.index(2), list.index(3), P::p)); }); @@ -192,7 +192,7 @@ public void testAccumulate5() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1, 2, 3, 4)), list.index(0).accumulate(list.index(1), list.index(2), list.index(3), list.index(4), P::p)); }); } @@ -203,7 +203,7 @@ public void testAccumulate6() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5)), list.index(0).accumulate(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), P::p)); }); } @@ -214,7 +214,7 @@ public void testAccumulate7() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5, 6)), list.index(0).accumulate(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), P::p)); }); } @@ -225,7 +225,7 @@ public void testAccumulate8() { .foldLeft(accumulator -> list -> accumulator.isEmpty() ? list.map(List::single) : accumulator.bind(accumulatorElement -> list.map(accumulatorElement::snoc)), List., Integer>>>nil()) - .forEach(list -> { + .foreachDoEffect(list -> { assertEquals(condition(list.forall(Validation::isSuccess), list.filter(Validation::isFail).map(validation -> validation.fail()), p(0, 1, 2, 3, 4, 5, 6, 7)), list.index(0).accumulate(list.index(1), list.index(2), list.index(3), list.index(4), list.index(5), list.index(6), list.index(7), P::p)); }); } From 8bf7192dec8054aa77b4db157e1237fc4670f7dd Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Sat, 13 Mar 2021 01:12:16 +1000 Subject: [PATCH 117/173] Update release notes for 4.8, 4.8.1 and release notes --- etc/release-notes/release-notes-4.8.1.adoc | 34 ++++++++++++++++++ etc/release-notes/release-notes-4.8.adoc | 41 +++++++++++++++++----- etc/release-process.txt | 35 ++++++++++++++++++ 3 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 etc/release-notes/release-notes-4.8.1.adoc diff --git a/etc/release-notes/release-notes-4.8.1.adoc b/etc/release-notes/release-notes-4.8.1.adoc new file mode 100644 index 00000000..45deb658 --- /dev/null +++ b/etc/release-notes/release-notes-4.8.1.adoc @@ -0,0 +1,34 @@ + += Release 4.8.1 + +Released: 8 Oct 2018 + +== Enhancements + +- Add Trampoline.suspend(final F0> a). See #367 https://github.com/functionaljava/functionaljava/pull/367. + +== Fixes + +- Fix regression in lifted semigroup sum. Fix #365, see #366 https://github.com/functionaljava/functionaljava/pull/366. + +== Internal + +- Fix compile under jdk11. Enable jdk11 travis build, see #361 https://github.com/functionaljava/functionaljava/pull/361. +- Fix warnings, see #369 https://github.com/functionaljava/functionaljava/pull/369. +- Add P tests, see #360 https://github.com/functionaljava/functionaljava/pull/360. +- Exclude consume/ from coverage, see #357 https://github.com/functionaljava/functionaljava/pull/357. +- Add DList tests, see #356 https://github.com/functionaljava/functionaljava/pull/356 +- Add Visitor tests, see #354 https://github.com/functionaljava/functionaljava/pull/354. + +== Breaking Changes + +* None. + +== Documentation + +* None. + +== Contributors + +* Jean Baptiste Giraudeau +* Gabor Liptak diff --git a/etc/release-notes/release-notes-4.8.adoc b/etc/release-notes/release-notes-4.8.adoc index 22dbc552..7e8aed61 100644 --- a/etc/release-notes/release-notes-4.8.adoc +++ b/etc/release-notes/release-notes-4.8.adoc @@ -1,28 +1,53 @@ -= Release += Release 4.8 -Proposed release: +Released: 18 Aug 2018 == Enhancements -* TODO. +- Enable upload of snapshot artifacts, see https://github.com/functionaljava/functionaljava/commit/e834e8b. +- Add append methods to all Px classes. Fix #326, see https://github.com/functionaljava/functionaljava/commit/065ed43. +- Introduce the Eval monad, see https://github.com/functionaljava/functionaljava/commit/98294fc. +- Fluent Equal/Ord construction, see #333 https://github.com/functionaljava/functionaljava/pull/333 +- Implement Zipper Eq and Hash and add tests, see #343 https://github.com/functionaljava/functionaljava/pull/343. +- Implement Vector equals, see #350 https://github.com/functionaljava/functionaljava/pull/350. == Fixes -* TODO. +- Fixed a bug in the NonEmptyList Semigroup implementation that resulted in the same NonEmptyList appended to itself. Regression in 4.7, see https://github.com/functionaljava/functionaljava/commit/07f94fa. +- Fixes #334: exception in Either.LeftProjection.traverseIO, see #335 https://github.com/functionaljava/functionaljava/pull/335 == Internal -* TODO. +- Added Scalacheck Arbitrary implementations for Natural and NonEmptyList, see https://github.com/functionaljava/functionaljava/commit/405c3ec +- Added unit test coverage for Semigroup implementations. The StringBuffer and StringBuilder tests fail because both of those types are mutable. The IO test fails because the ArbitraryIO implementation does not implement equals. See https://github.com/functionaljava/functionaljava/commit/ef81130. +- Fixed the ArbitraryIO implementation and created a Properties object for testing the IO semigroup. See https://github.com/functionaljava/functionaljava/commit/a8e979f. +- Equal: remove reference to static field of LazyString. Fix #321, see https://github.com/functionaljava/functionaljava/commit/6c6dabd. +- Add IOFunctions tests, see #340 https://github.com/functionaljava/functionaljava/pull/340. +- Add Stream tests, see #341 https://github.com/functionaljava/functionaljava/pull/341. +- Add tests for Try, F, FW, Digit. See #346 https://github.com/functionaljava/functionaljava/pull/346 +- Add Vector tests, see #347 https://github.com/functionaljava/functionaljava/pull/347 +- Add Optic tests, see #348 https://github.com/functionaljava/functionaljava/pull/348 +- Add Parser tests, see #349 https://github.com/functionaljava/functionaljava/pull/349 +- Add FingerTree tests, see #351 https://github.com/functionaljava/functionaljava/pull/351 +- Add TreeZipper tests, see #352 https://github.com/functionaljava/functionaljava/pull/352 +- Add Reader/Writer tests, see #353 https://github.com/functionaljava/functionaljava/pull/353 == Breaking Changes -* TODO. +None. == Documentation -* TODO. +None. == Contributors -* TODO. +* Jean Baptiste Giraudeau +* Ryan Johnson +* l1cache (cache@bk.ru) +* Gabor Liptak +* janbols +* Iaroslav Zeigerman +* Signey Quitorio + diff --git a/etc/release-process.txt b/etc/release-process.txt index e9bdbb15..2a87eef2 100644 --- a/etc/release-process.txt +++ b/etc/release-process.txt @@ -41,3 +41,38 @@ Update the website and Github README.adoc. This includes adding any features to Send a message to the group and social media about the release, TODO. +Setup Artifact Signing +====================== +The below text is a summary from https://gist.github.com/phit/bd3c6d156a2fa5f3b1bc15fa94b3256c. + +As of 2021-02-12, for Windows download Gpg4win 3.1.15 at https://gpg4win.org/index.html. You need to provide 3 things: +- the public key id +- the path to the secret key ring file for your private key +- the passphrase for your private key + +Open a command prompt and run "gpg --gen-key" and follow the prompts. +Get your key id by running: "gpg --list-key" + +Example output: + +gpg: checking the trustdb +gpg: marginals needed: 3 completes needed: 1 trust model: pgp +gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u +gpg: next trustdb check due at 2019-06-17 +C:/Users/phit/AppData/Roaming/gnupg/pubring.kbx +----------------------------------------------- +pub rsa2048 2017-06-17 [SC] [expires: 2019-06-17] + 77273D57FA5140E5A91905087A1B92B81840D019 +uid [ultimate] phit@hush.com +sub rsa2048 2017-06-17 [E] [expires: 2019-06-17] + +In this case we only have one key, 77273D57FA5140E5A91905087A1B92B81840D019 or short* 1840D019 which is basically just the last 8 characters of the long ID. + +Export the key using "gpg --export-secret-key > %UserProfile%\secring.gpg" + +In %UserProfile%\.gradle\gradle.properties, set the values below: + +signing.keyId=XXXXXXXX +signing.password=mypassword +signing.secretKeyRingFile=path/to/secring.gpg + From e9aa23acf3d2b53b9b114dd2e75f163028e52c42 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Sat, 13 Mar 2021 01:15:54 +1000 Subject: [PATCH 118/173] Removed obsolete change log file and directed from the readme to the release notes dir --- ChangeLog.md | 52 ---------------------------------------------------- README.adoc | 4 ++++ 2 files changed, 4 insertions(+), 52 deletions(-) delete mode 100644 ChangeLog.md diff --git a/ChangeLog.md b/ChangeLog.md deleted file mode 100644 index acdbedea..00000000 --- a/ChangeLog.md +++ /dev/null @@ -1,52 +0,0 @@ -4.8.1 ------ - -### Enhancements - -- Add Trampoline.suspend(final F0> a). (see [#367](https://github.com/functionaljava/functionaljava/pull/367)); - -### Fixes - -- Fix regression in lifted semigroup sum. Fix #365 (see [#366](https://github.com/functionaljava/functionaljava/pull/366)); - -### Internal - -- Fix compile under jdk11. Enable jdk11 travis build. (see [#361](https://github.com/functionaljava/functionaljava/pull/361)); -- Fix warnings (see [#369](https://github.com/functionaljava/functionaljava/pull/369)); -- Add P tests (see [#360](https://github.com/functionaljava/functionaljava/pull/360)); -- Exclude consume/ from coverage (see [#357](https://github.com/functionaljava/functionaljava/pull/357)); -- Add DList tests (see [#356](https://github.com/functionaljava/functionaljava/pull/356)); -- Add Visitor tests (see [#354](https://github.com/functionaljava/functionaljava/pull/354)); - -4.8 ---- - -### Enhancements - -- Enable upload of snapshot artifacts. (see [`e834e8b`](https://github.com/functionaljava/functionaljava/commit/e834e8b)); -- Add append methods to all Px classes. Fix #326 (see [`065ed43`](https://github.com/functionaljava/functionaljava/commit/065ed43)); -- Introduce the Eval monad (see [`98294fc`](https://github.com/functionaljava/functionaljava/commit/98294fc)); -- Fluent Equal/Ord construction (see [#333](https://github.com/functionaljava/functionaljava/pull/333)); -- Implement Zipper Eq and Hash and add tests (see [#343](https://github.com/functionaljava/functionaljava/pull/343)); -- Implement Vector equals (see [#350](https://github.com/functionaljava/functionaljava/pull/350)); - -### Fixes - -- Fixed a bug in the NonEmptyList Semigroup implementation that resulted in the same NonEmptyList appended to itself. (Regression in 4.7, see [`07f94fa`](https://github.com/functionaljava/functionaljava/commit/07f94fa)); -- Fixes #334: exception in Either.LeftProjection.traverseIO (see [#335](https://github.com/functionaljava/functionaljava/pull/335)); - -### Internal - -- Added Scalacheck Arbitrary implementations for Natural and NonEmptyList. (see [`405c3ec`](https://github.com/functionaljava/functionaljava/commit/405c3ec)); -- Added unit test coverage for Semigroup implementations. The StringBuffer and StringBuilder tests fail because both of those types are mutable. The IO test fails because the ArbitraryIO implementation does not implement equals. (see [`ef81130`](https://github.com/functionaljava/functionaljava/commit/ef81130)); -- Fixed the ArbitraryIO implementation and created a Properties object for testing the IO semigroup. (see [`a8e979f`](https://github.com/functionaljava/functionaljava/commit/a8e979f)); -- Equal: remove reference to static field of LazyString. Fix #321 (see [`6c6dabd`](https://github.com/functionaljava/functionaljava/commit/6c6dabd)); -- Add IOFunctions tests (see [#340](https://github.com/functionaljava/functionaljava/pull/340)); -- Add Stream tests (see [#341](https://github.com/functionaljava/functionaljava/pull/341)); -- Add tests for Try, F, FW, Digit (see [#346](https://github.com/functionaljava/functionaljava/pull/346)); -- Add Vector tests (see [#347](https://github.com/functionaljava/functionaljava/pull/347)); -- Add Optic tests (see [#348](https://github.com/functionaljava/functionaljava/pull/348)); -- Add Parser tests (see [#349](https://github.com/functionaljava/functionaljava/pull/349)); -- Add FingerTree tests (see [#351](https://github.com/functionaljava/functionaljava/pull/351)); -- Add TreeZipper tests (see [#352](https://github.com/functionaljava/functionaljava/pull/352)); -- Add Reader/Writer tests (see [#353](https://github.com/functionaljava/functionaljava/pull/353)); diff --git a/README.adoc b/README.adoc index 8a3ad347..cb53d003 100644 --- a/README.adoc +++ b/README.adoc @@ -122,3 +122,7 @@ A more complete description of the features mentioned above are: == License link:etc/LICENCE[The Functional Java license] uses the BSD 3 license (3-clause license) available at https://en.wikipedia.org/wiki/BSD_licenses[]. + +== Release Notes + +For release notes for each version, see the directory etc/release-notes. \ No newline at end of file From 9da757d0777a6bd19b37c52e44ceefec1bcb26d5 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Sat, 13 Mar 2021 01:20:56 +1000 Subject: [PATCH 119/173] Prepare for 4.9 release --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index c9d8be40..c264be1c 100644 --- a/build.gradle +++ b/build.gradle @@ -47,7 +47,7 @@ allprojects { defaultTasks "build" ext { - isSnapshot = true + isSnapshot = false fjBaseVersion = "4.9" snapshotAppendix = "-SNAPSHOT" @@ -55,7 +55,7 @@ allprojects { fjConsumeVersion = "4.8.1" signModule = false - useRetroLambda = false + useRetroLambda = true projectTitle = "Functional Java" projectName = "functionaljava" From c18ab08039573891c54ddd88cc41a77844587272 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Sat, 13 Mar 2021 01:43:16 +1000 Subject: [PATCH 120/173] Created 4.9 release notes --- etc/release-notes/release-notes-4.9.adoc | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 etc/release-notes/release-notes-4.9.adoc diff --git a/etc/release-notes/release-notes-4.9.adoc b/etc/release-notes/release-notes-4.9.adoc new file mode 100644 index 00000000..62db8e99 --- /dev/null +++ b/etc/release-notes/release-notes-4.9.adoc @@ -0,0 +1,42 @@ + += Release 4.9 + +Released: 14 March 2021 + +== Enhancements + +* Added Gen.streamOf(Gen) +* Added Option.sequence(Validation>) +* Added Gen.sequence(Validation>) +* Added Validation sequence and traverse functions to support various types. Added success and fails functions. +* Added Option sequence and traverse functions for various types. +* Added Seq.bind. +* Added List sequence and traverse functions for various types. +* Added Ord.seqOrd +* Added Seq sequence and traverse functions for various types. +* Added functions to Either. +* Added State bind synonym for flatMap. +* Added Steam sequence and traverse functions for various types. + +== Fixes + +* Fixed Validation.accumulate functions. + +== Internal + +* Support JPMS modules through 'Automatic-Module-Name'. + +== Breaking Changes + +* None. + +== Documentation + +* None. + +== Contributors + +* Jean Baptiste Giraudeau +* Gregoire Neuville +* Drew Taylor +* Mark Perry From c7dbd3fda84fec43b3104e0b60dc2a316a98fff4 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Sat, 13 Mar 2021 17:30:24 +1000 Subject: [PATCH 121/173] Prep for 4.9 release --- etc/release-process.txt | 17 +++++++++++++++++ gradle.properties | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/etc/release-process.txt b/etc/release-process.txt index 2a87eef2..b22ec4e4 100644 --- a/etc/release-process.txt +++ b/etc/release-process.txt @@ -76,3 +76,20 @@ signing.keyId=XXXXXXXX signing.password=mypassword signing.secretKeyRingFile=path/to/secring.gpg +Upload your key + +C:\repos\functionaljava>gpg --list-key +C:/Users/maper/AppData/Roaming/gnupg/pubring.kbx +------------------------------------------------ +pub rsa3072 2021-02-12 [SC] [expires: 2023-02-12] + E86A4EC34F25A9CF6118582A7985AAE03F41B2F9 +uid [ultimate] Mark Perry +sub rsa3072 2021-02-12 [E] [expires: 2023-02-12] + + +C:\repos\functionaljava>gpg --keyserver hkp://keyserver.ubuntu.com --send-keys E86A4EC34F25A9CF6118582A7985AAE03F41B2F9 +gpg: sending key 7985AAE03F41B2F9 to hkp://keyserver.ubuntu.com + +gradle upload (takes about 3 mins) + + diff --git a/gradle.properties b/gradle.properties index d4552a30..07c2b60c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,4 @@ sonatypeUsername = incorrectUser sonatypePassword = incorrectPwd -signingEnabled = false +signingEnabled = true From bcb4d199d2a82eafa7adf1b6b91246db29e45818 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Thu, 1 Apr 2021 17:45:39 +1000 Subject: [PATCH 122/173] Upgrade from Gradle 5.6.2 to 6.8.3 --- build.gradle | 26 +++++++++++------------- gradle/wrapper/gradle-wrapper.properties | 2 +- props-core-scalacheck/build.gradle | 4 ++++ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index d1b829d3..bcd01f5e 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,7 @@ buildscript { dependencies { classpath "com.github.ben-manes:gradle-versions-plugin:0.27.0" + classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:5.3.0" } wrapper { @@ -20,6 +21,7 @@ buildscript { } } + if (JavaVersion.current().isJava8Compatible()) { allprojects { tasks.withType(Javadoc) { @@ -117,8 +119,8 @@ task coverage(type: org.gradle.testing.jacoco.tasks.JacocoReport) { executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec") // We only care about coverage of: def projectForFoverage = ["core", "quickcheck", "java-core"] - classDirectories = files(subprojects.findAll {subproject -> subproject.name in projectForFoverage} .sourceSets.main.output) - sourceDirectories = files(subprojects.findAll {subproject -> subproject.name in projectForFoverage} .sourceSets.main.allSource.srcDirs) + getClassDirectories().from(files(subprojects.findAll {subproject -> subproject.name in projectForFoverage} .sourceSets.main.output)) + getSourceDirectories().from(files(subprojects.findAll {subproject -> subproject.name in projectForFoverage} .sourceSets.main.allSource.srcDirs)) reports { html.enabled = true @@ -130,8 +132,7 @@ configure(subprojects.findAll { it.name != "props-core" }) { apply plugin: "maven" apply plugin: "signing" - apply plugin: "osgi" - + apply plugin: "biz.aQute.bnd.builder" sourceCompatibility = "1.8" javadoc { @@ -156,16 +157,13 @@ configure(subprojects.findAll { it.name != "props-core" }) { jar { version project.fjVersion - manifest { - name = 'Functional Java' - instruction 'Signature-Version', project.fjVersion - instruction 'Bundle-ActivationPolicy', 'lazy' - instruction 'Bundle-Vendor', 'functionaljava.org' - if(project.name != "core") { - instruction 'Require-Bundle', 'org.functionaljava;bundle-version="'+project.fjBaseVersion+'"' - } - instruction 'Automatic-Module-Name', "functionaljava${project.name == 'core' ? '' : ".$project.name"}" - } + bnd ( + 'Bundle-Name': 'Functional Java', + 'Signature-Version': project.fjVersion, + 'Bundle-ActivationPolicy': 'lazy', + 'Bundle-Vendor': 'functionaljava.org', + 'Automatic-Module-Name': "functionaljava${project.name == 'core' ? '' : ".$project.name"}", + ) } eclipse { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ca9d6281..8cf6eb5a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/props-core-scalacheck/build.gradle b/props-core-scalacheck/build.gradle index 5207bf50..f26b5425 100644 --- a/props-core-scalacheck/build.gradle +++ b/props-core-scalacheck/build.gradle @@ -19,5 +19,9 @@ dependencies { testRuntime junitRuntime } +tasks.withType(ScalaCompile) { + scalaCompileOptions.additionalParameters = ["-feature", "-language:implicitConversions", "-language:postfixOps"] +} + performSigning(signingEnabled, signModule) configureUpload(signingEnabled, signModule) From 3ddcfe6e15ec840d27719c47c67be493061552d0 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Thu, 1 Apr 2021 17:46:08 +1000 Subject: [PATCH 123/173] Target Java 8 JVM --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index bcd01f5e..62f48dde 100644 --- a/build.gradle +++ b/build.gradle @@ -106,7 +106,7 @@ subprojects { } tasks.withType(JavaCompile) { - options.compilerArgs.addAll(['--release', '10']) + options.compilerArgs.addAll(['--release', '8']) if (displayCompilerWarnings) { options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" } From 851245c47700063529f55ac416dea09cb55c7bc2 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Thu, 1 Apr 2021 17:52:35 +1000 Subject: [PATCH 124/173] Don't sign modules by default --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 07c2b60c..d4552a30 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,4 @@ sonatypeUsername = incorrectUser sonatypePassword = incorrectPwd -signingEnabled = true +signingEnabled = false From fe4607210b0fdff9d1824b2e166a084bcd3ec32b Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Thu, 1 Apr 2021 17:52:54 +1000 Subject: [PATCH 125/173] Updated gradle version to 6.8.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 62f48dde..57c36c18 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { } wrapper { - gradleVersion = "5.6.2" + gradleVersion = "6.8.3" distributionType = Wrapper.DistributionType.ALL } } From 50aee0c7e35e128b5bdce7b8278ee98d27b16c4a Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 2 Apr 2021 00:15:18 +1000 Subject: [PATCH 126/173] Removed F1Functions, F1W, F2Functions and F2W --- core/src/main/java/fj/F.java | 681 ++++++++- core/src/main/java/fj/F1Functions.java | 687 --------- core/src/main/java/fj/F1W.java | 687 --------- core/src/main/java/fj/F2.java | 274 +++- core/src/main/java/fj/F2Functions.java | 273 ---- core/src/main/java/fj/F2W.java | 254 --- core/src/main/java/fj/Monoid.java | 4 +- core/src/main/java/fj/P1.java | 2 +- core/src/main/java/fj/P2.java | 4 +- core/src/main/java/fj/P3.java | 6 +- core/src/main/java/fj/P4.java | 8 +- core/src/main/java/fj/P5.java | 10 +- core/src/main/java/fj/P6.java | 12 +- core/src/main/java/fj/P7.java | 14 +- core/src/main/java/fj/P8.java | 16 +- core/src/main/java/fj/Semigroup.java | 5 +- core/src/main/java/fj/control/Trampoline.java | 9 +- .../java/fj/control/parallel/ParModule.java | 4 +- core/src/main/java/fj/data/IOFunctions.java | 5 +- core/src/main/java/fj/data/Iteratee.java | 3 +- core/src/main/java/fj/data/List.java | 2 +- core/src/main/java/fj/data/NonEmptyList.java | 3 +- core/src/main/java/fj/data/Reader.java | 3 +- core/src/main/java/fj/data/Tree.java | 4 +- core/src/main/java/fj/data/TreeMap.java | 7 +- core/src/main/java/fj/data/TreeZipper.java | 1356 ++++++++--------- core/src/main/java/fj/data/Zipper.java | 1170 +++++++------- .../java/fj/data/fingertrees/FingerTree.java | 536 +++---- core/src/test/java/fj/FFunctionsTest.java | 90 +- core/src/test/java/fj/FWFunctionsTest.java | 23 - .../test/java/fj/data/IOFunctionsTest.java | 3 +- .../test/java/fj/function/StringsTest.java | 3 +- .../main/java/fj/demo/Comonad_example.java | 3 +- demo/src/main/java/fj/demo/IODemo.java | 5 +- demo/src/main/java/fj/demo/IOWalkthrough.java | 7 +- demo/src/main/java/fj/demo/Primes2.java | 4 +- .../main/java/fj/demo/WriterDemo_Halver.java | 3 +- .../java/fj/demo/concurrent/MapReduce.java | 15 +- .../src/main/java/fj/demo/euler/Problem2.java | 6 +- .../src/test/scala/fj/data/CheckHashMap.scala | 236 +-- .../test/scala/fj/data/CheckIteratee.scala | 3 +- .../src/test/java/fj/data/ReaderTest.java | 6 +- 42 files changed, 2730 insertions(+), 3716 deletions(-) delete mode 100644 core/src/main/java/fj/F1Functions.java delete mode 100644 core/src/main/java/fj/F1W.java delete mode 100644 core/src/main/java/fj/F2Functions.java delete mode 100644 core/src/main/java/fj/F2W.java delete mode 100644 core/src/test/java/fj/FWFunctionsTest.java diff --git a/core/src/main/java/fj/F.java b/core/src/main/java/fj/F.java index e2db316e..d2208236 100644 --- a/core/src/main/java/fj/F.java +++ b/core/src/main/java/fj/F.java @@ -1,12 +1,27 @@ package fj; +import fj.control.parallel.Actor; +import fj.control.parallel.Promise; +import fj.control.parallel.Strategy; +import fj.data.*; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.TreeSet; +import java.util.concurrent.*; +import java.util.function.Function; + +import static fj.data.Option.some; +import static fj.data.Stream.iterableStream; +import static fj.data.Zipper.fromStream; + /** * A transformation or function from A to B. This type can be represented * using the Java 7 closure syntax. * * @version %build.number% */ -public interface F { +public interface F extends Function { /** * Transform A to B. * @@ -15,4 +30,668 @@ public interface F { */ B f(A a); + default B apply(A a) { + return f(a); + } + + /** + * Function composition + * + * @param g A function to compose with this one. + * @return The composed function such that this function is applied last. + */ + default F o(final F g) { + return c -> f(g.f(c)); + } + + /** + * First-class function composition + * + * @return A function that composes this function with another. + */ + default F, F> o() { + return g -> o(g); + } + + /** + * Function composition flipped. + * + * @param g A function with which to compose this one. + * @return The composed function such that this function is applied first. + */ + @SuppressWarnings("unchecked") + default F andThen(final F g) { + return g.o(this); + } + + /** + * First-class composition flipped. + * + * @return A function that invokes this function and then a given function on the result. + */ + default F, F> andThen() { + return g -> andThen(g); + } + + /** + * Binds a given function across this function (Reader Monad). + * + * @param g A function that takes the return value of this function as an argument, yielding a new function. + * @return A function that invokes this function on its argument and then the given function on the result. + */ + default F bind(final F> g) { + return a -> g.f(f(a)).f(a); + } + + /** + * First-class function binding. + * + * @return A function that binds another function across this function. + */ + default F>, F> bind() { + return g -> bind(g); + } + + /** + * Function application in an environment (Applicative Functor). + * + * @param g A function with the same argument type as this function, yielding a function that takes the return + * value of this function. + * @return A new function that invokes the given function on its argument, yielding a new function that is then + * applied to the result of applying this function to the argument. + */ + default F apply(final F> g) { + return a -> g.f(a).f(f(a)); + } + + /** + * First-class function application in an environment. + * + * @return A function that applies a given function within the environment of this function. + */ + default F>, F> apply() { + return g -> apply(g); + } + + /** + * Applies this function over the arguments of another function. + * + * @param g The function over whose arguments to apply this function. + * @return A new function that invokes this function on its arguments before invoking the given function. + */ + default F> on(final F> g) { + return a1 -> a2 -> g.f(f(a1)).f(f(a2)); + } + + + + /** + * Applies this function over the arguments of another function. + * + * @return A function that applies this function over the arguments of another function. + */ + default F>, F>> on() { + return g -> on(g); + } + + /** + * Promotes this function so that it returns its result in a product-1. Kleisli arrow for P1. + * + * @return This function promoted to return its result in a product-1. + */ + default F> lazy() { + return a -> P.lazy(() -> f(a)); + } + + /** + * Partial application. + * + * @param a The A to which to apply this function. + * @return The function partially applied to the given argument to return a lazy value. + */ + default P1 partial(final A a) { + return P.lazy(() -> f(a)); + } + + /** + * Promotes this function to map over a product-1. + * + * @return This function promoted to map over a product-1. + */ + default F, P1> mapP1() { + return p -> p.map(this); + } + + /** + * Promotes this function so that it returns its result in an Option. Kleisli arrow for Option. + * + * @return This function promoted to return its result in an Option. + */ + default F> optionK() { + return a -> some(f(a)); + } + + /** + * Promotes this function to map over an optional value. + * + * @return This function promoted to map over an optional value. + */ + default F, Option> mapOption() { + return o -> o.map(this); + } + + /** + * Promotes this function so that it returns its result in a List. Kleisli arrow for List. + * + * @return This function promoted to return its result in a List. + */ + default F> listK() { + return a -> List.single(f(a)); + } + + /** + * Promotes this function to map over a List. + * + * @return This function promoted to map over a List. + */ + default F, List> mapList() { + return x -> x.map(this); + } + + /** + * Promotes this function so that it returns its result in a Stream. Kleisli arrow for Stream. + * + * @return This function promoted to return its result in a Stream. + */ + default F> streamK() { + return a -> Stream.single(f(a)); + } + + /** + * Promotes this function to map over a Stream. + * + * @return This function promoted to map over a Stream. + */ + default F, Stream> mapStream() { + return x -> x.map(this); + } + + /** + * Promotes this function so that it returns its result in a Array. Kleisli arrow for Array. + * + * @return This function promoted to return its result in a Array. + */ + default F> arrayK() { + return a -> Array.single(f(a)); + + } + + /** + * Promotes this function to map over a Array. + * + * @return This function promoted to map over a Array. + */ + default F, Array> mapArray() { + return x -> x.map(this); + } + + /** + * Returns a function that contramaps over a given actor. + * + * @return A function that contramaps over a given actor. + */ + default F, Actor> contramapActor() { + return a -> a.contramap(this); + } + + /** + * Promotes this function to a concurrent function that returns a Promise of a value. + * + * @param s A parallel strategy for concurrent execution. + * @return A concurrent function that returns a Promise of a value. + */ + default F> promiseK(final Strategy s) { + return Promise.promise(s, this); + } + + /** + * Promotes this function to map over a Promise. + * + * @return This function promoted to map over Promises. + */ + default F, Promise> mapPromise() { + return p -> p.fmap(this); + } + + /** + * Promotes this function so that it returns its result on the left side of an Either. + * Kleisli arrow for the Either left projection. + * + * @return This function promoted to return its result on the left side of an Either. + */ + @SuppressWarnings("unchecked") + default F> eitherLeftK() { + return Either.left_().o(this); + } + + /** + * Promotes this function so that it returns its result on the right side of an Either. + * Kleisli arrow for the Either right projection. + * + * @return This function promoted to return its result on the right side of an Either. + */ + @SuppressWarnings("unchecked") + default F> eitherRightK() { + return Either.right_().o(this); + } + + /** + * Promotes this function to map over the left side of an Either. + * + * @return This function promoted to map over the left side of an Either. + */ + @SuppressWarnings("unchecked") + default F, Either> mapLeft() { + return Either.leftMap_().f(this); + } + + /** + * Promotes this function to map over the right side of an Either. + * + * @return This function promoted to map over the right side of an Either. + */ + @SuppressWarnings("unchecked") + default F, Either> mapRight() { + return Either.rightMap_().f(this); + } + + /** + * Returns a function that returns the left side of a given Either, or this function applied to the right side. + * + * @return a function that returns the left side of a given Either, or this function applied to the right side. + */ + default F, B> onLeft() { + return e -> e.left().on(this); + } + + /** + * Returns a function that returns the right side of a given Either, or this function applied to the left side. + * + * @return a function that returns the right side of a given Either, or this function applied to the left side. + */ + default F, B> onRight() { + return e -> e.right().on(this); + } + + /** + * Promotes this function to return its value in an Iterable. + * + * @return This function promoted to return its value in an Iterable. + */ + @SuppressWarnings("unchecked") + default F> iterableK() { + return IterableW.arrow().f(this); + } + + /** + * Promotes this function to map over Iterables. + * + * @return This function promoted to map over Iterables. + */ + @SuppressWarnings("unchecked") + default F, IterableW> mapIterable() { + return IterableW.map().f(this).o(IterableW.wrap()); + } + + /** + * Promotes this function to return its value in a NonEmptyList. + * + * @return This function promoted to return its value in a NonEmptyList. + */ + @SuppressWarnings("unchecked") + default F> nelK() { + return NonEmptyList.nel().o(this); + } + + /** + * Promotes this function to map over a NonEmptyList. + * + * @return This function promoted to map over a NonEmptyList. + */ + default F, NonEmptyList> mapNel() { + return list -> list.map(this); + } + + /** + * Promotes this function to return its value in a Set. + * + * @param o An order for the set. + * @return This function promoted to return its value in a Set. + */ + default F> setK(final Ord o) { + return a -> Set.single(o, f(a)); + } + + /** + * Promotes this function to map over a Set. + * + * @param o An order for the resulting set. + * @return This function promoted to map over a Set. + */ + default F, Set> mapSet(final Ord o) { + return s -> s.map(o, this); + } + + /** + * Promotes this function to return its value in a Tree. + * + * @return This function promoted to return its value in a Tree. + */ + default F> treeK() { + return a -> Tree.leaf(f(a)); + } + + /** + * Promotes this function to map over a Tree. + * + * @return This function promoted to map over a Tree. + */ + @SuppressWarnings("unchecked") + default F, Tree> mapTree() { + return Tree.fmap_().f(this); + } + + /** + * Returns a function that maps this function over a tree and folds it with the given monoid. + * + * @param m The monoid with which to fold the mapped tree. + * @return a function that maps this function over a tree and folds it with the given monoid. + */ + default F, B> foldMapTree(final Monoid m) { + return Tree.foldMap_(this, m); + } + + /** + * Promotes this function to return its value in a TreeZipper. + * + * @return This function promoted to return its value in a TreeZipper. + */ + default F> treeZipperK() { + return treeK().andThen(TreeZipper.fromTree()); + } + + /** + * Promotes this function to map over a TreeZipper. + * + * @return This function promoted to map over a TreeZipper. + */ + default F, TreeZipper> mapTreeZipper() { + return z -> z.map(this); + } + + /** + * Promotes this function so that it returns its result on the failure side of a Validation. + * Kleisli arrow for the Validation failure projection. + * + * @return This function promoted to return its result on the failure side of a Validation. + */ + default F> failK() { + return a -> Validation.fail(f(a)); + + } + + /** + * Promotes this function so that it returns its result on the success side of an Validation. + * Kleisli arrow for the Validation success projection. + * + * @return This function promoted to return its result on the success side of an Validation. + */ + default F> successK() { + return a -> Validation.success(f(a)); + } + + /** + * Promotes this function to map over the failure side of a Validation. + * + * @return This function promoted to map over the failure side of a Validation. + */ + default F, Validation> mapFail() { + return v -> v.f().map(this); + } + + /** + * Promotes this function to map over the success side of a Validation. + * + * @return This function promoted to map over the success side of a Validation. + */ + default F, Validation> mapSuccess() { + return v -> v.map(this); + } + + /** + * Returns a function that returns the failure side of a given Validation, + * or this function applied to the success side. + * + * @return a function that returns the failure side of a given Validation, + * or this function applied to the success side. + */ + default F, B> onFail() { + return v -> v.f().on(this); + } + + /** + * Returns a function that returns the success side of a given Validation, + * or this function applied to the failure side. + * + * @return a function that returns the success side of a given Validation, + * or this function applied to the failure side. + */ + default F, B> onSuccess() { + return v -> v.on(this); + } + + /** + * Promotes this function to return its value in a Zipper. + * + * @return This function promoted to return its value in a Zipper. + */ + default F> zipperK() { + return streamK().andThen(s -> fromStream(s).some()); + } + + /** + * Promotes this function to map over a Zipper. + * + * @return This function promoted to map over a Zipper. + */ + default F, Zipper> mapZipper() { + return z -> z.map(this); + } + + /** + * Promotes this function to map over an Equal as a contravariant functor. + * + * @return This function promoted to map over an Equal as a contravariant functor. + */ + default F, Equal> contramapEqual() { + return e -> e.contramap(this); + } + + /** + * Promotes this function to map over a Hash as a contravariant functor. + * + * @return This function promoted to map over a Hash as a contravariant functor. + */ + default F, Hash> contramapHash() { + return h -> h.contramap(this); + } + + /** + * Promotes this function to map over a Show as a contravariant functor. + * + * @return This function promoted to map over a Show as a contravariant functor. + */ + default F, Show> contramapShow() { + return s -> s.contramap(this); + } + + /** + * Promotes this function to map over the first element of a pair. + * + * @return This function promoted to map over the first element of a pair. + */ + default F, P2> mapFst() { + return P2.map1_(this); + } + + /** + * Promotes this function to map over the second element of a pair. + * + * @return This function promoted to map over the second element of a pair. + */ + default F, P2> mapSnd() { + return P2.map2_(this); + } + + /** + * Promotes this function to map over both elements of a pair. + * + * @return This function promoted to map over both elements of a pair. + */ + default F, P2> mapBoth() { + return p2 -> P2.map(this, p2); + } + + /** + * Maps this function over a SynchronousQueue. + * + * @param as A SynchronousQueue to map this function over. + * @return A new SynchronousQueue with this function applied to each element. + */ + default SynchronousQueue mapJ(final SynchronousQueue as) { + final SynchronousQueue bs = new SynchronousQueue<>(); + bs.addAll(iterableStream(as).map(this).toCollection()); + return bs; + } + + + /** + * Maps this function over a PriorityBlockingQueue. + * + * @param as A PriorityBlockingQueue to map this function over. + * @return A new PriorityBlockingQueue with this function applied to each element. + */ + default PriorityBlockingQueue mapJ(final PriorityBlockingQueue as) { + return new PriorityBlockingQueue<>(iterableStream(as).map(this).toCollection()); + } + + /** + * Maps this function over a LinkedBlockingQueue. + * + * @param as A LinkedBlockingQueue to map this function over. + * @return A new LinkedBlockingQueue with this function applied to each element. + */ + default LinkedBlockingQueue mapJ(final LinkedBlockingQueue as) { + return new LinkedBlockingQueue<>(iterableStream(as).map(this).toCollection()); + } + + /** + * Maps this function over a CopyOnWriteArraySet. + * + * @param as A CopyOnWriteArraySet to map this function over. + * @return A new CopyOnWriteArraySet with this function applied to each element. + */ + default CopyOnWriteArraySet mapJ(final CopyOnWriteArraySet as) { + return new CopyOnWriteArraySet<>(iterableStream(as).map(this).toCollection()); + } + + /** + * Maps this function over a CopyOnWriteArrayList. + * + * @param as A CopyOnWriteArrayList to map this function over. + * @return A new CopyOnWriteArrayList with this function applied to each element. + */ + default CopyOnWriteArrayList mapJ(final CopyOnWriteArrayList as) { + return new CopyOnWriteArrayList<>(iterableStream(as).map(this).toCollection()); + } + + /** + * Maps this function over a ConcurrentLinkedQueue. + * + * @param as A ConcurrentLinkedQueue to map this function over. + * @return A new ConcurrentLinkedQueue with this function applied to each element. + */ + default ConcurrentLinkedQueue mapJ(final ConcurrentLinkedQueue as) { + return new ConcurrentLinkedQueue<>(iterableStream(as).map(this).toCollection()); + } + + /** + * Maps this function over an ArrayBlockingQueue. + * + * @param as An ArrayBlockingQueue to map this function over. + * @return A new ArrayBlockingQueue with this function applied to each element. + */ + default ArrayBlockingQueue mapJ(final ArrayBlockingQueue as) { + final ArrayBlockingQueue bs = new ArrayBlockingQueue<>(as.size()); + bs.addAll(iterableStream(as).map(this).toCollection()); + return bs; + } + + + /** + * Maps this function over a TreeSet. + * + * @param as A TreeSet to map this function over. + * @return A new TreeSet with this function applied to each element. + */ + default TreeSet mapJ(final TreeSet as) { + return new TreeSet<>(iterableStream(as).map(this).toCollection()); + } + + /** + * Maps this function over a PriorityQueue. + * + * @param as A PriorityQueue to map this function over. + * @return A new PriorityQueue with this function applied to each element. + */ + default java.util.PriorityQueue mapJ(final java.util.PriorityQueue as) { + return new java.util.PriorityQueue<>(iterableStream(as).map(this).toCollection()); + } + + /** + * Maps this function over a LinkedList. + * + * @param as A LinkedList to map this function over. + * @return A new LinkedList with this function applied to each element. + */ + default LinkedList mapJ(final LinkedList as) { + return new LinkedList<>(iterableStream(as).map(this).toCollection()); + } + + /** + * Maps this function over an ArrayList. + * + * @param as An ArrayList to map this function over. + * @return A new ArrayList with this function applied to each element. + */ + default ArrayList mapJ(final ArrayList as) { + return new ArrayList<>(iterableStream(as).map(this).toCollection()); + } + + default F map(F f) { + return f.o(this); + } + + default F contramap(F f) { + return o(f); + } + + /** + * Both map (with g) and contramap (with f) the target function. (Profunctor pattern) + */ + default F dimap(F f, F g) { + return c -> g.f(f(f.f(c))); + } + + } diff --git a/core/src/main/java/fj/F1Functions.java b/core/src/main/java/fj/F1Functions.java deleted file mode 100644 index 00bdf515..00000000 --- a/core/src/main/java/fj/F1Functions.java +++ /dev/null @@ -1,687 +0,0 @@ -package fj; - -import fj.control.parallel.Actor; -import fj.control.parallel.Promise; -import fj.control.parallel.Strategy; -import fj.data.*; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.PriorityQueue; -import java.util.TreeSet; -import java.util.concurrent.*; - -import static fj.data.Option.some; -import static fj.data.Stream.iterableStream; -import static fj.data.Zipper.fromStream; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public final class F1Functions { - - - private F1Functions() { - } - - /** - * Function composition - * - * @param g A function to compose with this one. - * @return The composed function such that this function is applied last. - */ - public static F o(final F f, final F g) { - return c -> f.f(g.f(c)); - } - - /** - * First-class function composition - * - * @return A function that composes this function with another. - */ - public static F, F> o(final F f) { - return g -> o(f, g); - } - - /** - * Function composition flipped. - * - * @param g A function with which to compose this one. - * @return The composed function such that this function is applied first. - */ - @SuppressWarnings("unchecked") - public static F andThen(final F f, final F g) { - return o(g, f); - } - - /** - * First-class composition flipped. - * - * @return A function that invokes this function and then a given function on the result. - */ - public static F, F> andThen(final F f) { - return g -> andThen(f, g); - } - - /** - * Binds a given function across this function (Reader Monad). - * - * @param g A function that takes the return value of this function as an argument, yielding a new function. - * @return A function that invokes this function on its argument and then the given function on the result. - */ - public static F bind(final F f, final F> g) { - return a -> g.f(f.f(a)).f(a); - } - - /** - * First-class function binding. - * - * @return A function that binds another function across this function. - */ - public static F>, F> bind(final F f) { - return g -> bind(f, g); - } - - /** - * Function application in an environment (Applicative Functor). - * - * @param g A function with the same argument type as this function, yielding a function that takes the return - * value of this function. - * @return A new function that invokes the given function on its argument, yielding a new function that is then - * applied to the result of applying this function to the argument. - */ - public static F apply(final F f, final F> g) { - return a -> g.f(a).f(f.f(a)); - } - - /** - * First-class function application in an environment. - * - * @return A function that applies a given function within the environment of this function. - */ - public static F>, F> apply(final F f) { - return g -> apply(f, g); - } - - /** - * Applies this function over the arguments of another function. - * - * @param g The function over whose arguments to apply this function. - * @return A new function that invokes this function on its arguments before invoking the given function. - */ - public static F> on(final F f, final F> g) { - return a1 -> a2 -> g.f(f.f(a1)).f(f.f(a2)); - } - - - - /** - * Applies this function over the arguments of another function. - * - * @return A function that applies this function over the arguments of another function. - */ - public static F>, F>> on(final F f) { - return g -> on(f, g); - } - - /** - * Promotes this function so that it returns its result in a product-1. Kleisli arrow for P1. - * - * @return This function promoted to return its result in a product-1. - */ - public static F> lazy(final F f) { - return a -> P.lazy(() -> f.f(a)); - } - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument to return a lazy value. - */ - public static P1 f(final F f, final A a) { - return P.lazy(() -> f.f(a)); - } - - /** - * Promotes this function to map over a product-1. - * - * @return This function promoted to map over a product-1. - */ - public static F, P1> mapP1(final F f) { - return p -> p.map(f); - } - - /** - * Promotes this function so that it returns its result in an Option. Kleisli arrow for Option. - * - * @return This function promoted to return its result in an Option. - */ - public static F> optionK(final F f) { - return a -> some(f.f(a)); - } - - /** - * Promotes this function to map over an optional value. - * - * @return This function promoted to map over an optional value. - */ - public static F, Option> mapOption(final F f) { - return o -> o.map(f); - } - - /** - * Promotes this function so that it returns its result in a List. Kleisli arrow for List. - * - * @return This function promoted to return its result in a List. - */ - public static F> listK(final F f) { - return a -> List.single(f.f(a)); - } - - /** - * Promotes this function to map over a List. - * - * @return This function promoted to map over a List. - */ - public static F, List> mapList(final F f) { - return x -> x.map(f); - } - - /** - * Promotes this function so that it returns its result in a Stream. Kleisli arrow for Stream. - * - * @return This function promoted to return its result in a Stream. - */ - public static F> streamK(final F f) { - return a -> Stream.single(f.f(a)); - } - - /** - * Promotes this function to map over a Stream. - * - * @return This function promoted to map over a Stream. - */ - public static F, Stream> mapStream(final F f) { - return x -> x.map(f); - } - - /** - * Promotes this function so that it returns its result in a Array. Kleisli arrow for Array. - * - * @return This function promoted to return its result in a Array. - */ - public static F> arrayK(final F f) { - return a -> Array.single(f.f(a)); - - } - - /** - * Promotes this function to map over a Array. - * - * @return This function promoted to map over a Array. - */ - public static F, Array> mapArray(final F f) { - return x -> x.map(f); - } - - /** - * Returns a function that contramaps over a given actor. - * - * @return A function that contramaps over a given actor. - */ - public static F, Actor> contramapActor(final F f) { - return a -> a.contramap(f); - } - - /** - * Promotes this function to a concurrent function that returns a Promise of a value. - * - * @param s A parallel strategy for concurrent execution. - * @return A concurrent function that returns a Promise of a value. - */ - public static F> promiseK(final F f, final Strategy s) { - return Promise.promise(s, f); - } - - /** - * Promotes this function to map over a Promise. - * - * @return This function promoted to map over Promises. - */ - public static F, Promise> mapPromise(final F f) { - return p -> p.fmap(f); - } - - /** - * Promotes this function so that it returns its result on the left side of an Either. - * Kleisli arrow for the Either left projection. - * - * @return This function promoted to return its result on the left side of an Either. - */ - @SuppressWarnings("unchecked") - public static F> eitherLeftK(final F f) { - return o(Either.left_(), f); - } - - /** - * Promotes this function so that it returns its result on the right side of an Either. - * Kleisli arrow for the Either right projection. - * - * @return This function promoted to return its result on the right side of an Either. - */ - @SuppressWarnings("unchecked") - public static F> eitherRightK(final F f) { - return o(Either.right_(), f); - } - - /** - * Promotes this function to map over the left side of an Either. - * - * @return This function promoted to map over the left side of an Either. - */ - @SuppressWarnings("unchecked") - public static F, Either> mapLeft(final F f) { - return Either.leftMap_().f(f); - } - - /** - * Promotes this function to map over the right side of an Either. - * - * @return This function promoted to map over the right side of an Either. - */ - @SuppressWarnings("unchecked") - public static F, Either> mapRight(final F f) { - return Either.rightMap_().f(f); - } - - /** - * Returns a function that returns the left side of a given Either, or this function applied to the right side. - * - * @return a function that returns the left side of a given Either, or this function applied to the right side. - */ - public static F, B> onLeft(final F f) { - return e -> e.left().on(f); - } - - /** - * Returns a function that returns the right side of a given Either, or this function applied to the left side. - * - * @return a function that returns the right side of a given Either, or this function applied to the left side. - */ - public static F, B> onRight(final F f) { - return e -> e.right().on(f); - } - - /** - * Promotes this function to return its value in an Iterable. - * - * @return This function promoted to return its value in an Iterable. - */ - @SuppressWarnings("unchecked") - public static F> iterableK(final F f) { - return IterableW.arrow().f(f); - } - - /** - * Promotes this function to map over Iterables. - * - * @return This function promoted to map over Iterables. - */ - @SuppressWarnings("unchecked") - public static F, IterableW> mapIterable(final F f) { - return o(IterableW.map().f(f), IterableW.wrap()); - } - - /** - * Promotes this function to return its value in a NonEmptyList. - * - * @return This function promoted to return its value in a NonEmptyList. - */ - @SuppressWarnings("unchecked") - public static F> nelK(final F f) { - return o(NonEmptyList.nel(), f); - } - - /** - * Promotes this function to map over a NonEmptyList. - * - * @return This function promoted to map over a NonEmptyList. - */ - public static F, NonEmptyList> mapNel(final F f) { - return list -> list.map(f); - } - - /** - * Promotes this function to return its value in a Set. - * - * @param o An order for the set. - * @return This function promoted to return its value in a Set. - */ - public static F> setK(final F f, final Ord o - ) { - return a -> Set.single(o, f.f(a)); - } - - /** - * Promotes this function to map over a Set. - * - * @param o An order for the resulting set. - * @return This function promoted to map over a Set. - */ - public static F, Set> mapSet(final F f, final Ord o) { - return s -> s.map(o, f); - } - - /** - * Promotes this function to return its value in a Tree. - * - * @return This function promoted to return its value in a Tree. - */ - public static F> treeK(final F f) { - return a -> Tree.leaf(f.f(a)); - } - - /** - * Promotes this function to map over a Tree. - * - * @return This function promoted to map over a Tree. - */ - @SuppressWarnings("unchecked") - public static F, Tree> mapTree(final F f) { - return Tree.fmap_().f(f); - } - - /** - * Returns a function that maps this function over a tree and folds it with the given monoid. - * - * @param m The monoid with which to fold the mapped tree. - * @return a function that maps this function over a tree and folds it with the given monoid. - */ - public static F, B> foldMapTree(final F f, final Monoid m) { - return Tree.foldMap_(f, m); - } - - /** - * Promotes this function to return its value in a TreeZipper. - * - * @return This function promoted to return its value in a TreeZipper. - */ - public static F> treeZipperK(final F f) { - return andThen(treeK(f), TreeZipper.fromTree()); - } - - /** - * Promotes this function to map over a TreeZipper. - * - * @return This function promoted to map over a TreeZipper. - */ - public static F, TreeZipper> mapTreeZipper(final F f) { - return (z) -> z.map(f); - } - - /** - * Promotes this function so that it returns its result on the failure side of a Validation. - * Kleisli arrow for the Validation failure projection. - * - * @return This function promoted to return its result on the failure side of a Validation. - */ - public static F> failK(final F f) { - return a -> Validation.fail(f.f(a)); - - } - - /** - * Promotes this function so that it returns its result on the success side of an Validation. - * Kleisli arrow for the Validation success projection. - * - * @return This function promoted to return its result on the success side of an Validation. - */ - public static F> successK(final F f) { - return a -> Validation.success(f.f(a)); - } - - /** - * Promotes this function to map over the failure side of a Validation. - * - * @return This function promoted to map over the failure side of a Validation. - */ - public static F, Validation> mapFail(final F f) { - return v -> v.f().map(f); - } - - /** - * Promotes this function to map over the success side of a Validation. - * - * @return This function promoted to map over the success side of a Validation. - */ - public static F, Validation> mapSuccess(final F f) { - return v -> v.map(f); - } - - /** - * Returns a function that returns the failure side of a given Validation, - * or this function applied to the success side. - * - * @return a function that returns the failure side of a given Validation, - * or this function applied to the success side. - */ - public static F, B> onFail(final F f) { - return v -> v.f().on(f); - } - - /** - * Returns a function that returns the success side of a given Validation, - * or this function applied to the failure side. - * - * @return a function that returns the success side of a given Validation, - * or this function applied to the failure side. - */ - public static F, B> onSuccess(final F f) { - return v -> v.on(f); - } - - /** - * Promotes this function to return its value in a Zipper. - * - * @return This function promoted to return its value in a Zipper. - */ - public static F> zipperK(final F f) { - return andThen(streamK(f), s -> fromStream(s).some()); - } - - /** - * Promotes this function to map over a Zipper. - * - * @return This function promoted to map over a Zipper. - */ - public static F, Zipper> mapZipper(final F f) { - return z -> z.map(f); - } - - /** - * Promotes this function to map over an Equal as a contravariant functor. - * - * @return This function promoted to map over an Equal as a contravariant functor. - */ - public static F, Equal> contramapEqual(final F f) { - return e -> e.contramap(f); - } - - /** - * Promotes this function to map over a Hash as a contravariant functor. - * - * @return This function promoted to map over a Hash as a contravariant functor. - */ - public static F, Hash> contramapHash(final F f) { - return h -> h.contramap(f); - } - - /** - * Promotes this function to map over a Show as a contravariant functor. - * - * @return This function promoted to map over a Show as a contravariant functor. - */ - public static F, Show> contramapShow(final F f) { - return s -> s.contramap(f); - } - - /** - * Promotes this function to map over the first element of a pair. - * - * @return This function promoted to map over the first element of a pair. - */ - public static F, P2> mapFst(final F f) { - return P2.map1_(f); - } - - /** - * Promotes this function to map over the second element of a pair. - * - * @return This function promoted to map over the second element of a pair. - */ - public static F, P2> mapSnd(final F f) { - return P2.map2_(f); - } - - /** - * Promotes this function to map over both elements of a pair. - * - * @return This function promoted to map over both elements of a pair. - */ - public static F, P2> mapBoth(final F f) { - return p2 -> P2.map(f, p2); - } - - /** - * Maps this function over a SynchronousQueue. - * - * @param as A SynchronousQueue to map this function over. - * @return A new SynchronousQueue with this function applied to each element. - */ - public static SynchronousQueue mapJ(final F f, final SynchronousQueue as) { - final SynchronousQueue bs = new SynchronousQueue<>(); - bs.addAll(iterableStream(as).map(f).toCollection()); - return bs; - } - - - /** - * Maps this function over a PriorityBlockingQueue. - * - * @param as A PriorityBlockingQueue to map this function over. - * @return A new PriorityBlockingQueue with this function applied to each element. - */ - public static PriorityBlockingQueue mapJ(final F f, final PriorityBlockingQueue as) { - return new PriorityBlockingQueue<>(iterableStream(as).map(f).toCollection()); - } - - /** - * Maps this function over a LinkedBlockingQueue. - * - * @param as A LinkedBlockingQueue to map this function over. - * @return A new LinkedBlockingQueue with this function applied to each element. - */ - public static LinkedBlockingQueue mapJ(final F f, final LinkedBlockingQueue as) { - return new LinkedBlockingQueue<>(iterableStream(as).map(f).toCollection()); - } - - /** - * Maps this function over a CopyOnWriteArraySet. - * - * @param as A CopyOnWriteArraySet to map this function over. - * @return A new CopyOnWriteArraySet with this function applied to each element. - */ - public static CopyOnWriteArraySet mapJ(final F f, final CopyOnWriteArraySet as) { - return new CopyOnWriteArraySet<>(iterableStream(as).map(f).toCollection()); - } - - /** - * Maps this function over a CopyOnWriteArrayList. - * - * @param as A CopyOnWriteArrayList to map this function over. - * @return A new CopyOnWriteArrayList with this function applied to each element. - */ - public static CopyOnWriteArrayList mapJ(final F f, final CopyOnWriteArrayList as) { - return new CopyOnWriteArrayList<>(iterableStream(as).map(f).toCollection()); - } - - /** - * Maps this function over a ConcurrentLinkedQueue. - * - * @param as A ConcurrentLinkedQueue to map this function over. - * @return A new ConcurrentLinkedQueue with this function applied to each element. - */ - public static ConcurrentLinkedQueue mapJ(final F f, final ConcurrentLinkedQueue as) { - return new ConcurrentLinkedQueue<>(iterableStream(as).map(f).toCollection()); - } - - /** - * Maps this function over an ArrayBlockingQueue. - * - * @param as An ArrayBlockingQueue to map this function over. - * @return A new ArrayBlockingQueue with this function applied to each element. - */ - public static ArrayBlockingQueue mapJ(final F f, final ArrayBlockingQueue as) { - final ArrayBlockingQueue bs = new ArrayBlockingQueue<>(as.size()); - bs.addAll(iterableStream(as).map(f).toCollection()); - return bs; - } - - - /** - * Maps this function over a TreeSet. - * - * @param as A TreeSet to map this function over. - * @return A new TreeSet with this function applied to each element. - */ - public static TreeSet mapJ(final F f, final TreeSet as) { - return new TreeSet<>(iterableStream(as).map(f).toCollection()); - } - - /** - * Maps this function over a PriorityQueue. - * - * @param as A PriorityQueue to map this function over. - * @return A new PriorityQueue with this function applied to each element. - */ - public static PriorityQueue mapJ(final F f, final PriorityQueue as) { - return new PriorityQueue<>(iterableStream(as).map(f).toCollection()); - } - - /** - * Maps this function over a LinkedList. - * - * @param as A LinkedList to map this function over. - * @return A new LinkedList with this function applied to each element. - */ - public static LinkedList mapJ(final F f, final LinkedList as) { - return new LinkedList<>(iterableStream(as).map(f).toCollection()); - } - - /** - * Maps this function over an ArrayList. - * - * @param as An ArrayList to map this function over. - * @return A new ArrayList with this function applied to each element. - */ - public static ArrayList mapJ(final F f, final ArrayList as) { - return new ArrayList<>(iterableStream(as).map(f).toCollection()); - } - - public static F map(F target, F f) { - return o(f, target); - } - - public static F contramap(F target, F f) { - return o(target, f); - } - - /** - * Both map (with g) and contramap (with f) the target function. (Profunctor pattern) - */ - public static F dimap(F target, F f, F g) { - return c -> g.f(target.f(f.f(c))); - } - -} diff --git a/core/src/main/java/fj/F1W.java b/core/src/main/java/fj/F1W.java deleted file mode 100644 index 611c9e01..00000000 --- a/core/src/main/java/fj/F1W.java +++ /dev/null @@ -1,687 +0,0 @@ -package fj; - -import fj.control.parallel.Actor; -import fj.control.parallel.Promise; -import fj.control.parallel.Strategy; -import fj.data.*; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.PriorityQueue; -import java.util.TreeSet; -import java.util.concurrent.*; - -/** - * Created by MarkPerry on 22/01/2015. - */ -public abstract class F1W implements F { - - /** - * Function composition - * - * @param g A function to compose with this one. - * @return The composed function such that this function is applied last. - */ - public final F1W o(final F g) { - return lift(F1Functions.o(this, g)); - } - - /** - * First-class function composition - * - * @return A function that composes this function with another. - */ - public final F1W, F> o() { - return lift(F1Functions.o(this)); - } - - /** - * Function composition flipped. - * - * @param g A function with which to compose this one. - * @return The composed function such that this function is applied first. - */ - @SuppressWarnings("unchecked") - public final F1W andThen(final F g) { - return lift(F1Functions.andThen(this, g)); - } - - /** - * First-class composition flipped. - * - * @return A function that invokes this function and then a given function on the result. - */ - public final F1W, F> andThen() { - return lift( F1Functions.andThen(this)); - } - - /** - * Binds a given function across this function (Reader Monad). - * - * @param g A function that takes the return value of this function as an argument, yielding a new function. - * @return A function that invokes this function on its argument and then the given function on the result. - */ - public final F1W bind(final F> g) { - return lift(F1Functions.bind(this, g)); - } - - - /** - * First-class function binding. - * - * @return A function that binds another function across this function. - */ - public final F1W>, F> bind() { - return lift(F1Functions.bind(this)); - } - - /** - * Function application in an environment (Applicative Functor). - * - * @param g A function with the same argument type as this function, yielding a function that takes the return - * value of this function. - * @return A new function that invokes the given function on its argument, yielding a new function that is then - * applied to the result of applying this function to the argument. - */ - public final F1W apply(final F> g) { - return lift(F1Functions.apply(this, g)); - } - - - /** - * First-class function application in an environment. - * - * @return A function that applies a given function within the environment of this function. - */ - public final F1W>, F> apply() { - return lift(F1Functions.apply(this)); - } - - /** - * Applies this function over the arguments of another function. - * - * @param g The function over whose arguments to apply this function. - * @return A new function that invokes this function on its arguments before invoking the given function. - */ - public final F1W> on(final F> g) { - return lift(F1Functions.on(this, g)); - } - - - /** - * Applies this function over the arguments of another function. - * - * @return A function that applies this function over the arguments of another function. - */ - public final F1W>, F>> on() { - return lift(F1Functions.on(this)); - } - - /** - * Promotes this function so that it returns its result in a product-1. Kleisli arrow for P1. - * - * @return This function promoted to return its result in a product-1. - */ - public final F1W> lazy() { - return lift(F1Functions.lazy(this)); - } - - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument to return a lazy value. - */ - public final P1 lazy(final A a) { - return F1Functions.f(this, a); - } - - /** - * Promotes this function to map over a product-1. - * - * @return This function promoted to map over a product-1. - */ - public final F1W, P1> mapP1() { - return lift(F1Functions.mapP1(this)); - } - - /** - * Promotes this function so that it returns its result in an Option. Kleisli arrow for Option. - * - * @return This function promoted to return its result in an Option. - */ - public final F1W> optionK() { - return lift(F1Functions.optionK(this)); - } - - - /** - * Promotes this function to map over an optional value. - * - * @return This function promoted to map over an optional value. - */ - public final F1W, Option> mapOption() { - return lift(F1Functions.mapOption(this)); - } - - /** - * Promotes this function so that it returns its result in a List. Kleisli arrow for List. - * - * @return This function promoted to return its result in a List. - */ - public final F1W> listK() { - return lift( F1Functions.listK(this)); - } - - /** - * Promotes this function to map over a List. - * - * @return This function promoted to map over a List. - */ - public final F1W, List> mapList() { - return lift(F1Functions.mapList(this)); - } - - /** - * Promotes this function so that it returns its result in a Stream. Kleisli arrow for Stream. - * - * @return This function promoted to return its result in a Stream. - */ - public final F1W> streamK() { - return lift(F1Functions.streamK(this)); - } - - /** - * Promotes this function to map over a Stream. - * - * @return This function promoted to map over a Stream. - */ - public final F1W, Stream> mapStream() { - return lift(F1Functions.mapStream(this)); - } - - /** - * Promotes this function so that it returns its result in a Array. Kleisli arrow for Array. - * - * @return This function promoted to return its result in a Array. - */ - public final F1W> arrayK() { - return lift(F1Functions.arrayK(this)); - - } - - /** - * Promotes this function to map over a Array. - * - * @return This function promoted to map over a Array. - */ - public final F1W, Array> mapArray() { - return lift(F1Functions.mapArray(this)); - } - - /** - * Returns a function that contramaps over a given actor. - * - * @return A function that contramaps over a given actor. - */ - public final F1W, Actor> contramapActor() { - return lift(F1Functions.contramapActor(this)); - } - - /** - * Promotes this function to a concurrent function that returns a Promise of a value. - * - * @param s A parallel strategy for concurrent execution. - * @return A concurrent function that returns a Promise of a value. - */ - public final F1W> promiseK(final Strategy s) { - return lift(F1Functions.promiseK(this, s)); - } - - /** - * Promotes this function to map over a Promise. - * - * @return This function promoted to map over Promises. - */ - public final F1W, Promise> mapPromise() { - return lift(F1Functions.mapPromise(this)); - } - - /** - * Promotes this function so that it returns its result on the left side of an Either. - * Kleisli arrow for the Either left projection. - * - * @return This function promoted to return its result on the left side of an Either. - */ - @SuppressWarnings("unchecked") - public final F1W> eitherLeftK() { - return lift(F1Functions.eitherLeftK(this)); - } - - /** - * Promotes this function so that it returns its result on the right side of an Either. - * Kleisli arrow for the Either right projection. - * - * @return This function promoted to return its result on the right side of an Either. - */ - @SuppressWarnings("unchecked") - public final F1W> eitherRightK() { - return lift(F1Functions.eitherRightK(this)); - } - - /** - * Promotes this function to map over the left side of an Either. - * - * @return This function promoted to map over the left side of an Either. - */ - @SuppressWarnings("unchecked") - public final F1W, Either> mapLeft() { - return lift(F1Functions.mapLeft(this)); - } - - /** - * Promotes this function to map over the right side of an Either. - * - * @return This function promoted to map over the right side of an Either. - */ - @SuppressWarnings("unchecked") - public final F1W, Either> mapRight() { - return lift(F1Functions.mapRight(this)); - } - - /** - * Returns a function that returns the left side of a given Either, or this function applied to the right side. - * - * @return a function that returns the left side of a given Either, or this function applied to the right side. - */ - public final F1W, B> onLeft() { - return lift(F1Functions.onLeft(this)); - } - - /** - * Returns a function that returns the right side of a given Either, or this function applied to the left side. - * - * @return a function that returns the right side of a given Either, or this function applied to the left side. - */ - public final F1W, B> onRight() { - return lift(F1Functions.onRight(this)); - } - - /** - * Promotes this function to return its value in an Iterable. - * - * @return This function promoted to return its value in an Iterable. - */ - @SuppressWarnings("unchecked") - public final F1W> iterableK() { - return lift( F1Functions.iterableK(this)); - } - - /** - * Promotes this function to map over Iterables. - * - * @return This function promoted to map over Iterables. - */ - @SuppressWarnings("unchecked") - public final F1W, IterableW> mapIterable() { - return lift( F1Functions.mapIterable(this)); - } - - /** - * Promotes this function to return its value in a NonEmptyList. - * - * @return This function promoted to return its value in a NonEmptyList. - */ - @SuppressWarnings("unchecked") - public final F1W> nelK() { - return lift(F1Functions.nelK(this)); - } - - /** - * Promotes this function to map over a NonEmptyList. - * - * @return This function promoted to map over a NonEmptyList. - */ - public final F1W, NonEmptyList> mapNel() { - return lift(F1Functions.mapNel(this)); - } - - /** - * Promotes this function to return its value in a Set. - * - * @param o An order for the set. - * @return This function promoted to return its value in a Set. - */ - public final F1W> setK(final Ord o) { - return lift(F1Functions.setK(this, o)); - } - - /** - * Promotes this function to map over a Set. - * - * @param o An order for the resulting set. - * @return This function promoted to map over a Set. - */ - public final F1W, Set> mapSet(final Ord o) { - return lift(F1Functions.mapSet(this, o)); - } - - /** - * Promotes this function to return its value in a Tree. - * - * @return This function promoted to return its value in a Tree. - */ - public final F1W> treeK() { - return lift(F1Functions.treeK(this)); - } - - /** - * Promotes this function to map over a Tree. - * - * @return This function promoted to map over a Tree. - */ - @SuppressWarnings("unchecked") - public final F1W, Tree> mapTree() { - return lift(F1Functions.mapTree(this)); - } - - /** - * Returns a function that maps this function over a tree and folds it with the given monoid. - * - * @param m The monoid with which to fold the mapped tree. - * @return a function that maps this function over a tree and folds it with the given monoid. - */ - public final F1W, B> foldMapTree(final Monoid m) { - return lift(F1Functions.foldMapTree(this, m)); - } - - /** - * Promotes this function to return its value in a TreeZipper. - * - * @return This function promoted to return its value in a TreeZipper. - */ - public final F1W> treeZipperK() { - return lift(F1Functions.treeZipperK(this)); - } - - /** - * Promotes this function to map over a TreeZipper. - * - * @return This function promoted to map over a TreeZipper. - */ - public final F1W, TreeZipper> mapTreeZipper() { - return lift(F1Functions.mapTreeZipper(this)); - } - - /** - * Promotes this function so that it returns its result on the failure side of a Validation. - * Kleisli arrow for the Validation failure projection. - * - * @return This function promoted to return its result on the failure side of a Validation. - */ - public final F1W> failK() { - return lift(F1Functions.failK(this)); - } - - /** - * Promotes this function so that it returns its result on the success side of an Validation. - * Kleisli arrow for the Validation success projection. - * - * @return This function promoted to return its result on the success side of an Validation. - */ - public final F1W> successK() { - return lift( F1Functions.successK(this)); - } - - /** - * Promotes this function to map over the failure side of a Validation. - * - * @return This function promoted to map over the failure side of a Validation. - */ - public final F1W, Validation> mapFail() { - return lift(F1Functions.mapFail(this)); - } - - /** - * Promotes this function to map over the success side of a Validation. - * - * @return This function promoted to map over the success side of a Validation. - */ - public final F1W, Validation> mapSuccess() { - return lift(F1Functions.mapSuccess(this)); - } - - /** - * Returns a function that returns the failure side of a given Validation, - * or this function applied to the success side. - * - * @return a function that returns the failure side of a given Validation, - * or this function applied to the success side. - */ - public final F1W, B> onFail() { - return lift(F1Functions.onFail(this)); - } - - /** - * Returns a function that returns the success side of a given Validation, - * or this function applied to the failure side. - * - * @return a function that returns the success side of a given Validation, - * or this function applied to the failure side. - */ - public final F1W, B> onSuccess() { - return lift(F1Functions.onSuccess(this)); - } - - /** - * Promotes this function to return its value in a Zipper. - * - * @return This function promoted to return its value in a Zipper. - */ - public final F1W> zipperK() { - return lift(F1Functions.zipperK(this)); - } - - /** - * Promotes this function to map over a Zipper. - * - * @return This function promoted to map over a Zipper. - */ - public final F1W, Zipper> mapZipper() { - return lift(F1Functions.mapZipper(this)); - } - - /** - * Promotes this function to map over an Equal as a contravariant functor. - * - * @return This function promoted to map over an Equal as a contravariant functor. - */ - public final F1W, Equal> contramapEqual() { - return lift(F1Functions.contramapEqual(this)); - } - - /** - * Promotes this function to map over a Hash as a contravariant functor. - * - * @return This function promoted to map over a Hash as a contravariant functor. - */ - public final F1W, Hash> contramapHash() { - return lift(F1Functions.contramapHash(this)); - } - - /** - * Promotes this function to map over a Show as a contravariant functor. - * - * @return This function promoted to map over a Show as a contravariant functor. - */ - public final F1W, Show> contramapShow() { - return lift(F1Functions.contramapShow(this)); - } - - /** - * Promotes this function to map over the first element of a pair. - * - * @return This function promoted to map over the first element of a pair. - */ - public final F1W, P2> mapFst() { - return lift(F1Functions.mapFst(this)); - } - - /** - * Promotes this function to map over the second element of a pair. - * - * @return This function promoted to map over the second element of a pair. - */ - public final F1W, P2> mapSnd() { - return lift(F1Functions.mapSnd(this)); - } - - /** - * Promotes this function to map over both elements of a pair. - * - * @return This function promoted to map over both elements of a pair. - */ - public final F1W, P2> mapBoth() { - return lift(F1Functions.mapBoth(this)); - } - - /** - * Maps this function over a SynchronousQueue. - * - * @param as A SynchronousQueue to map this function over. - * @return A new SynchronousQueue with this function applied to each element. - */ - public final SynchronousQueue mapJ(final SynchronousQueue as) { - return F1Functions.mapJ(this, as); - } - - - /** - * Maps this function over a PriorityBlockingQueue. - * - * @param as A PriorityBlockingQueue to map this function over. - * @return A new PriorityBlockingQueue with this function applied to each element. - */ - public final PriorityBlockingQueue mapJ(final PriorityBlockingQueue as) { - return F1Functions.mapJ(this, as); - } - - /** - * Maps this function over a LinkedBlockingQueue. - * - * @param as A LinkedBlockingQueue to map this function over. - * @return A new LinkedBlockingQueue with this function applied to each element. - */ - public final LinkedBlockingQueue mapJ(final LinkedBlockingQueue as) { - return F1Functions.mapJ(this, as); - } - - /** - * Maps this function over a CopyOnWriteArraySet. - * - * @param as A CopyOnWriteArraySet to map this function over. - * @return A new CopyOnWriteArraySet with this function applied to each element. - */ - public final CopyOnWriteArraySet mapJ(final CopyOnWriteArraySet as) { - return F1Functions.mapJ(this, as); - } - - /** - * Maps this function over a CopyOnWriteArrayList. - * - * @param as A CopyOnWriteArrayList to map this function over. - * @return A new CopyOnWriteArrayList with this function applied to each element. - */ - public final CopyOnWriteArrayList mapJ(final CopyOnWriteArrayList as) { - return F1Functions.mapJ(this, as); - } - - /** - * Maps this function over a ConcurrentLinkedQueue. - * - * @param as A ConcurrentLinkedQueue to map this function over. - * @return A new ConcurrentLinkedQueue with this function applied to each element. - */ - public final ConcurrentLinkedQueue mapJ(final ConcurrentLinkedQueue as) { - return F1Functions.mapJ(this, as); - } - - /** - * Maps this function over an ArrayBlockingQueue. - * - * @param as An ArrayBlockingQueue to map this function over. - * @return A new ArrayBlockingQueue with this function applied to each element. - */ - public final ArrayBlockingQueue mapJ(final ArrayBlockingQueue as) { - return F1Functions.mapJ(this, as); - } - - - /** - * Maps this function over a TreeSet. - * - * @param as A TreeSet to map this function over. - * @return A new TreeSet with this function applied to each element. - */ - public final TreeSet mapJ(final TreeSet as) { - return F1Functions.mapJ(this, as); - } - - /** - * Maps this function over a PriorityQueue. - * - * @param as A PriorityQueue to map this function over. - * @return A new PriorityQueue with this function applied to each element. - */ - public final PriorityQueue mapJ(final PriorityQueue as) { - return F1Functions.mapJ(this, as); - } - - /** - * Maps this function over a LinkedList. - * - * @param as A LinkedList to map this function over. - * @return A new LinkedList with this function applied to each element. - */ - public final LinkedList mapJ(final LinkedList as) { - return F1Functions.mapJ(this, as); - } - - /** - * Maps this function over an ArrayList. - * - * @param as An ArrayList to map this function over. - * @return A new ArrayList with this function applied to each element. - */ - public final ArrayList mapJ(final ArrayList as) { - return F1Functions.mapJ(this, as); - } - - public final F1W map(F f) { - return lift(F1Functions.map(this, f)); - } - - public final F1W contramap(F f) { - return lift(F1Functions.contramap(this, f)); - } - - public static class F1WFunc extends F1W { - final F func; - public F1WFunc(F f) { - func = f; - } - - @Override - public final B f(A a) { - return func.f(a); - } - } - - /** - * Lifts the function into the fully featured function wrapper - */ - public static F1W lift(final F f) { - return new F1WFunc<>(f); - } -} diff --git a/core/src/main/java/fj/F2.java b/core/src/main/java/fj/F2.java index 63555fd5..ba0e6f2f 100644 --- a/core/src/main/java/fj/F2.java +++ b/core/src/main/java/fj/F2.java @@ -1,12 +1,24 @@ package fj; +import fj.control.parallel.Promise; +import fj.data.*; + +import java.util.function.BiFunction; + +import static fj.P.p; +import static fj.data.IterableW.wrap; +import static fj.data.Set.iterableSet; +import static fj.data.Tree.node; +import static fj.data.TreeZipper.treeZipper; +import static fj.data.Zipper.zipper; + /** * A transformation function of arity-2 from A and B to C. * This type can be represented using the Java 7 closure syntax. * * @version %build.number% */ -public interface F2 { +public interface F2 extends BiFunction { /** * Transform A and B to C. * @@ -16,4 +28,264 @@ public interface F2 { */ C f(A a, B b); + default C apply(A a, B b) { + return f(a, b); + } + + /** + * Partial application. + * + * @param a The A to which to apply this function. + * @return The function partially applied to the given argument. + */ + default F f(final A a) { + return b -> f(a, b); + } + + /** + * Curries this wrapped function to a wrapped function of arity-1 that returns another wrapped function. + * + * @return a wrapped function of arity-1 that returns another wrapped function. + */ + default F> curry() { + return a -> b -> f(a, b); + } + + /** + * Flips the arguments of this function. + * + * @return A new function with the arguments of this function flipped. + */ + default F2 flip() { + return (b, a) -> f(a, b); + } + + /** + * Uncurries this function to a function on tuples. + * + * @return A new function that calls this function with the elements of a given tuple. + */ + default F, C> tuple() { + return p -> f(p._1(), p._2()); + } + + /** + * Promotes this function to a function on Arrays. + * + * @return This function promoted to transform Arrays. + */ + default F2, Array, Array> arrayM() { + return (a, b) -> a.bind(b, curry()); + } + + /** + * Promotes this function to a function on Promises. + * + * @return This function promoted to transform Promises. + */ + default F2, Promise, Promise> promiseM() { + return (a, b) -> a.bind(b, curry()); + } + + /** + * Promotes this function to a function on Iterables. + * + * @return This function promoted to transform Iterables. + */ + default F2, Iterable, IterableW> iterableM() { + return (a, b) -> IterableW.liftM2(curry()).f(a).f(b); + } + + /** + * Promotes this function to a function on Lists. + * + * @return This function promoted to transform Lists. + */ + default F2, List, List> listM() { + return (a, b) -> List.liftM2(curry()).f(a).f(b); + } + + /** + * Promotes this function to a function on non-empty lists. + * + * @return This function promoted to transform non-empty lists. + */ + default F2, NonEmptyList, NonEmptyList> nelM() { + return (as, bs) -> NonEmptyList.fromList(as.toList().bind(bs.toList(), this)).some(); + } + + /** + * Promotes this function to a function on Options. + * + * @return This function promoted to transform Options. + */ + default F2, Option, Option> optionM() { + return (a, b) -> Option.liftM2(curry()).f(a).f(b); + } + + /** + * Promotes this function to a function on Sets. + * + * @param o An ordering for the result of the promoted function. + * @return This function promoted to transform Sets. + */ + default F2, Set, Set> setM(final Ord o) { + return (as, bs) -> { + Set cs = Set.empty(o); + for (final A a : as) + for (final B b : bs) + cs = cs.insert(f(a, b)); + return cs; + }; + } + + /** + * Promotes this function to a function on Streams. + * + * @return This function promoted to transform Streams. + */ + default F2, Stream, Stream> streamM() { + return (as, bs) -> as.bind(bs, this); + } + + /** + * Promotes this function to a function on Trees. + * + * @return This function promoted to transform Trees. + */ + default F2, Tree, Tree> treeM() { + F2 outer = this; + return new F2, Tree, Tree>() { + public Tree f(final Tree as, final Tree bs) { + final F2, Tree, Tree> self = this; + return node(outer.f(as.root(), bs.root()), P.lazy(() -> self.streamM().f(as.subForest()._1(), bs.subForest()._1()))); + } + }; + } + + /** + * Promotes this function to zip two arrays, applying the function lock-step over both Arrays. + * + * @return A function that zips two arrays with this function. + */ + default F2, Array, Array> zipArrayM() { + return (as, bs) -> as.zipWith(bs, this); + } + + /** + * Promotes this function to zip two iterables, applying the function lock-step over both iterables. + * + * @return A function that zips two iterables with this function. + */ + default F2, Iterable, Iterable> zipIterableM() { + return (as, bs) -> wrap(as).zipWith(bs, this); + } + + /** + * Promotes this function to zip two lists, applying the function lock-step over both lists. + * + * @return A function that zips two lists with this function. + */ + default F2, List, List> zipListM() { + return (as, bs) -> as.zipWith(bs, this); + } + + + /** + * Promotes this function to zip two streams, applying the function lock-step over both streams. + * + * @return A function that zips two streams with this function. + */ + default F2, Stream, Stream> zipStreamM() { + return (as, bs) -> as.zipWith(bs, this); + } + + /** + * Promotes this function to zip two non-empty lists, applying the function lock-step over both lists. + * + * @return A function that zips two non-empty lists with this function. + */ + default F2, NonEmptyList, NonEmptyList> zipNelM() { + return (as, bs) -> NonEmptyList.fromList(as.toList().zipWith(bs.toList(), this)).some(); + } + + /** + * Promotes this function to zip two sets, applying the function lock-step over both sets. + * + * @param o An ordering for the resulting set. + * @return A function that zips two sets with this function. + */ + default F2, Set, Set> zipSetM(final Ord o) { + return (as, bs) -> iterableSet(o, as.toStream().zipWith(bs.toStream(), this)); + } + + /** + * Promotes this function to zip two trees, applying the function lock-step over both trees. + * The structure of the resulting tree is the structural intersection of the two trees. + * + * @return A function that zips two trees with this function. + */ + default F2, Tree, Tree> zipTreeM() { + F2 outer = this; + return new F2, Tree, Tree>() { + public Tree f(final Tree ta, final Tree tb) { + final F2, Tree, Tree> self = this; + return node(outer.f(ta.root(), tb.root()), P.lazy(() -> self.zipStreamM().f(ta.subForest()._1(), tb.subForest()._1()))); + } + }; + } + + /** + * Promotes this function to zip two zippers, applying the function lock-step over both zippers in both directions. + * The structure of the resulting zipper is the structural intersection of the two zippers. + * + * @return A function that zips two zippers with this function. + */ + default F2, Zipper, Zipper> zipZipperM() { + return (ta, tb) -> { + final F2, Stream, Stream> sf = this.zipStreamM(); + return zipper(sf.f(ta.lefts(), tb.lefts()), f(ta.focus(), tb.focus()), sf.f(ta.rights(), tb.rights())); + }; + } + + /** + * Promotes this function to zip two TreeZippers, applying the function lock-step over both zippers in all directions. + * The structure of the resulting TreeZipper is the structural intersection of the two TreeZippers. + * + * @return A function that zips two TreeZippers with this function. + */ + default F2, TreeZipper, TreeZipper> zipTreeZipperM() { + F2 outer = this; + return (ta, tb) -> { + final F2>, Stream>, Stream>> sf = outer.treeM().zipStreamM(); + F2>, A, Stream>>, P3>, B, Stream>>, P3>, C, Stream>>> g = + (pa, pb) -> p(outer.treeM().zipStreamM().f(pa._1(), pb._1()), outer.f(pa._2(), pb._2()), + outer.treeM().zipStreamM().f(pa._3(), pb._3())); + final + F2>, A, Stream>>>, + Stream>, B, Stream>>>, + Stream>, C, Stream>>>> + pf = g.zipStreamM(); + return treeZipper(outer.treeM().f(ta.p()._1(), tb.p()._1()), sf.f(ta.lefts(), tb.lefts()), + sf.f(ta.rights(), tb.rights()), pf.f(ta.p()._4(), tb.p()._4())); + }; + } + + default F2 contramapFirst(F f) { + return (z, b) -> f(f.f(z), b); + } + + default F2 contramapSecond(F f) { + return (a, z) -> f(a, f.f(z)); + } + + default F2 contramap(F f, F g) { + return contramapFirst(f).contramapSecond(g); + } + + default F2 map(F f) { + return (a, b) -> f.f(f(a, b)); + } + + } diff --git a/core/src/main/java/fj/F2Functions.java b/core/src/main/java/fj/F2Functions.java deleted file mode 100644 index f69e02ed..00000000 --- a/core/src/main/java/fj/F2Functions.java +++ /dev/null @@ -1,273 +0,0 @@ -package fj; - -import fj.control.parallel.Promise; -import fj.data.*; - -import static fj.P.p; -import static fj.data.IterableW.wrap; -import static fj.data.Set.iterableSet; -import static fj.data.Tree.node; -import static fj.data.TreeZipper.treeZipper; -import static fj.data.Zipper.zipper; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public final class F2Functions { - - - private F2Functions() { - } - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - public static F f(final F2 f, final A a) { - return b -> f.f(a, b); - } - - /** - * Curries this wrapped function to a wrapped function of arity-1 that returns another wrapped function. - * - * @return a wrapped function of arity-1 that returns another wrapped function. - */ - public static F> curry(final F2 f) { - return a -> b -> f.f(a, b); - } - - /** - * Flips the arguments of this function. - * - * @return A new function with the arguments of this function flipped. - */ - public static F2 flip(final F2 f) { - return (b, a) -> f.f(a, b); - } - - /** - * Uncurries this function to a function on tuples. - * - * @return A new function that calls this function with the elements of a given tuple. - */ - public static F, C> tuple(final F2 f) { - return p -> f.f(p._1(), p._2()); - } - - /** - * Promotes this function to a function on Arrays. - * - * @return This function promoted to transform Arrays. - */ - public static F2, Array, Array> arrayM(final F2 f) { - return (a, b) -> a.bind(b, curry(f)); - } - - /** - * Promotes this function to a function on Promises. - * - * @return This function promoted to transform Promises. - */ - public static F2, Promise, Promise> promiseM(final F2 f) { - return (a, b) -> a.bind(b, curry(f)); - } - - /** - * Promotes this function to a function on Iterables. - * - * @return This function promoted to transform Iterables. - */ - public static F2, Iterable, IterableW> iterableM(final F2 f) { - return (a, b) -> IterableW.liftM2(curry(f)).f(a).f(b); - } - - /** - * Promotes this function to a function on Lists. - * - * @return This function promoted to transform Lists. - */ - public static F2, List, List> listM(final F2 f) { - return (a, b) -> List.liftM2(curry(f)).f(a).f(b); - } - - /** - * Promotes this function to a function on non-empty lists. - * - * @return This function promoted to transform non-empty lists. - */ - public static F2, NonEmptyList, NonEmptyList> nelM(final F2 f) { - return (as, bs) -> NonEmptyList.fromList(as.toList().bind(bs.toList(), f)).some(); - } - - /** - * Promotes this function to a function on Options. - * - * @return This function promoted to transform Options. - */ - public static F2, Option, Option> optionM(final F2 f) { - return (a, b) -> Option.liftM2(curry(f)).f(a).f(b); - } - - /** - * Promotes this function to a function on Sets. - * - * @param o An ordering for the result of the promoted function. - * @return This function promoted to transform Sets. - */ - public static F2, Set, Set> setM(final F2 f, final Ord o) { - return (as, bs) -> { - Set cs = Set.empty(o); - for (final A a : as) - for (final B b : bs) - cs = cs.insert(f.f(a, b)); - return cs; - }; - } - - /** - * Promotes this function to a function on Streams. - * - * @return This function promoted to transform Streams. - */ - public static F2, Stream, Stream> streamM(final F2 f) { - return (as, bs) -> as.bind(bs, f); - } - - /** - * Promotes this function to a function on Trees. - * - * @return This function promoted to transform Trees. - */ - public static F2, Tree, Tree> treeM(final F2 f) { - return new F2, Tree, Tree>() { - public Tree f(final Tree as, final Tree bs) { - final F2, Tree, Tree> self = this; - return node(f.f(as.root(), bs.root()), P.lazy(() -> streamM(self).f(as.subForest()._1(), bs.subForest()._1()))); - } - }; - } - - /** - * Promotes this function to zip two arrays, applying the function lock-step over both Arrays. - * - * @return A function that zips two arrays with this function. - */ - public static F2, Array, Array> zipArrayM(final F2 f) { - return (as, bs) -> as.zipWith(bs, f); - } - - /** - * Promotes this function to zip two iterables, applying the function lock-step over both iterables. - * - * @return A function that zips two iterables with this function. - */ - public static F2, Iterable, Iterable> zipIterableM(final F2 f) { - return (as, bs) -> wrap(as).zipWith(bs, f); - } - - /** - * Promotes this function to zip two lists, applying the function lock-step over both lists. - * - * @return A function that zips two lists with this function. - */ - public static F2, List, List> zipListM(final F2 f) { - return (as, bs) -> as.zipWith(bs, f); - } - - - /** - * Promotes this function to zip two streams, applying the function lock-step over both streams. - * - * @return A function that zips two streams with this function. - */ - public static F2, Stream, Stream> zipStreamM(final F2 f) { - return (as, bs) -> as.zipWith(bs, f); - } - - /** - * Promotes this function to zip two non-empty lists, applying the function lock-step over both lists. - * - * @return A function that zips two non-empty lists with this function. - */ - public static F2, NonEmptyList, NonEmptyList> zipNelM(final F2 f) { - return (as, bs) -> NonEmptyList.fromList(as.toList().zipWith(bs.toList(), f)).some(); - } - - /** - * Promotes this function to zip two sets, applying the function lock-step over both sets. - * - * @param o An ordering for the resulting set. - * @return A function that zips two sets with this function. - */ - public static F2, Set, Set> zipSetM(final F2 f, final Ord o) { - return (as, bs) -> iterableSet(o, as.toStream().zipWith(bs.toStream(), f)); - } - - /** - * Promotes this function to zip two trees, applying the function lock-step over both trees. - * The structure of the resulting tree is the structural intersection of the two trees. - * - * @return A function that zips two trees with this function. - */ - public static F2, Tree, Tree> zipTreeM(final F2 f) { - return new F2, Tree, Tree>() { - public Tree f(final Tree ta, final Tree tb) { - final F2, Tree, Tree> self = this; - return node(f.f(ta.root(), tb.root()), P.lazy(() -> zipStreamM(self).f(ta.subForest()._1(), tb.subForest()._1()))); - } - }; - } - - /** - * Promotes this function to zip two zippers, applying the function lock-step over both zippers in both directions. - * The structure of the resulting zipper is the structural intersection of the two zippers. - * - * @return A function that zips two zippers with this function. - */ - public static F2, Zipper, Zipper> zipZipperM(final F2 f) { - return (ta, tb) -> { - final F2, Stream, Stream> sf = zipStreamM(f); - return zipper(sf.f(ta.lefts(), tb.lefts()), f.f(ta.focus(), tb.focus()), sf.f(ta.rights(), tb.rights())); - }; - } - - /** - * Promotes this function to zip two TreeZippers, applying the function lock-step over both zippers in all directions. - * The structure of the resulting TreeZipper is the structural intersection of the two TreeZippers. - * - * @return A function that zips two TreeZippers with this function. - */ - public static F2, TreeZipper, TreeZipper> zipTreeZipperM(final F2 f) { - return (ta, tb) -> { - final F2>, Stream>, Stream>> sf = zipStreamM(treeM(f)); - final - F2>, A, Stream>>>, - Stream>, B, Stream>>>, - Stream>, C, Stream>>>> - pf = - zipStreamM((pa, pb) -> p(zipStreamM(treeM(f)).f(pa._1(), pb._1()), f.f(pa._2(), pb._2()), - zipStreamM(treeM(f)).f(pa._3(), pb._3()))); - return treeZipper(treeM(f).f(ta.p()._1(), tb.p()._1()), sf.f(ta.lefts(), tb.lefts()), - sf.f(ta.rights(), tb.rights()), pf.f(ta.p()._4(), tb.p()._4())); - }; - } - - public static F2 contramapFirst(F2 target, F f) { - return (z, b) -> target.f(f.f(z), b); - } - - public static F2 contramapSecond(F2 target, F f) { - return (a, z) -> target.f(a, f.f(z)); - } - - public static F2 contramap(F2 target, F f, F g) { - return contramapSecond(contramapFirst(target, f), g); - } - - public static F2 map(F2 target, F f) { - return (a, b) -> f.f(target.f(a, b)); - } - -} diff --git a/core/src/main/java/fj/F2W.java b/core/src/main/java/fj/F2W.java deleted file mode 100644 index 1f35855a..00000000 --- a/core/src/main/java/fj/F2W.java +++ /dev/null @@ -1,254 +0,0 @@ -package fj; - -import fj.control.parallel.Promise; -import fj.data.*; - -/** - * Created by MarkPerry on 22/01/2015. - */ -public abstract class F2W implements F2 { - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - public final F1W f(final A a) { - return F1W.lift(F2Functions.f(this, a)); - } - - /** - * Curries this wrapped function to a wrapped function of arity-1 that returns another wrapped function. - * - * @return a wrapped function of arity-1 that returns another wrapped function. - */ - public final F1W> curry() { - return F1W.lift(F2Functions.curry(this)); - } - - /** - * Flips the arguments of this function. - * - * @return A new function with the arguments of this function flipped. - */ - public final F2W flip() { - return lift(F2Functions.flip(this)); - } - - /** - * Uncurries this function to a function on tuples. - * - * @return A new function that calls this function with the elements of a given tuple. - */ - public final F1W, C> tuple() { - return F1W.lift(F2Functions.tuple(this)); - } - - /** - * Promotes this function to a function on Arrays. - * - * @return This function promoted to transform Arrays. - */ - public final F2W, Array, Array> arrayM() { - return lift(F2Functions.arrayM(this)); - } - - /** - * Promotes this function to a function on Promises. - * - * @return This function promoted to transform Promises. - */ - public final F2W, Promise, Promise> promiseM() { - return lift(F2Functions.promiseM(this)); - } - - /** - * Promotes this function to a function on Iterables. - * - * @return This function promoted to transform Iterables. - */ - public final F2W, Iterable, IterableW> iterableM() { - return lift(F2Functions.iterableM(this)); - } - - /** - * Promotes this function to a function on Lists. - * - * @return This function promoted to transform Lists. - */ - public final F2W, List, List> listM() { - return lift(F2Functions.listM(this)); - } - - /** - * Promotes this function to a function on non-empty lists. - * - * @return This function promoted to transform non-empty lists. - */ - public final F2W, NonEmptyList, NonEmptyList> nelM() { - return lift(F2Functions.nelM(this)); - } - - /** - * Promotes this function to a function on Options. - * - * @return This function promoted to transform Options. - */ - public final F2W, Option, Option> optionM() { - return lift(F2Functions.optionM(this)); - } - - /** - * Promotes this function to a function on Sets. - * - * @param o An ordering for the result of the promoted function. - * @return This function promoted to transform Sets. - */ - public final F2W, Set, Set> setM(final Ord o) { - return lift(F2Functions.setM(this, o)); - } - - /** - * Promotes this function to a function on Streams. - * - * @return This function promoted to transform Streams. - */ - public final F2W, Stream, Stream> streamM() { - return lift(F2Functions.streamM(this)); - } - - /** - * Promotes this function to a function on Trees. - * - * @return This function promoted to transform Trees. - */ - public final F2W, Tree, Tree> treeM() { - return lift(F2Functions.treeM(this)); - } - - /** - * Promotes this function to zip two arrays, applying the function lock-step over both Arrays. - * - * @return A function that zips two arrays with this function. - */ - public final F2W, Array, Array> zipArrayM() { - return lift(F2Functions.zipArrayM(this)); - } - - /** - * Promotes this function to zip two iterables, applying the function lock-step over both iterables. - * - * @return A function that zips two iterables with this function. - */ - public final F2W, Iterable, Iterable> zipIterableM() { - return lift(F2Functions.zipIterableM(this)); - } - - /** - * Promotes this function to zip two lists, applying the function lock-step over both lists. - * - * @return A function that zips two lists with this function. - */ - public final F2W, List, List> zipListM() { - return lift(F2Functions.zipListM(this)); - } - - - /** - * Promotes this function to zip two streams, applying the function lock-step over both streams. - * - * @return A function that zips two streams with this function. - */ - public final F2W, Stream, Stream> zipStreamM() { - return lift(F2Functions.zipStreamM(this)); - } - - /** - * Promotes this function to zip two non-empty lists, applying the function lock-step over both lists. - * - * @return A function that zips two non-empty lists with this function. - */ - public final F2W, NonEmptyList, NonEmptyList> zipNelM() { - return lift(F2Functions.zipNelM(this)); - } - - /** - * Promotes this function to zip two sets, applying the function lock-step over both sets. - * - * @param o An ordering for the resulting set. - * @return A function that zips two sets with this function. - */ - public final F2W, Set, Set> zipSetM(final Ord o) { - return lift(F2Functions.zipSetM(this, o)); - } - - /** - * Promotes this function to zip two trees, applying the function lock-step over both trees. - * The structure of the resulting tree is the structural intersection of the two trees. - * - * @return A function that zips two trees with this function. - */ - public final F2W, Tree, Tree> zipTreeM() { - return lift(F2Functions.zipTreeM(this)); - } - - /** - * Promotes this function to zip two zippers, applying the function lock-step over both zippers in both directions. - * The structure of the resulting zipper is the structural intersection of the two zippers. - * - * @return A function that zips two zippers with this function. - */ - public final F2W, Zipper, Zipper> zipZipperM() { - return lift(F2Functions.zipZipperM(this)); - } - - /** - * Promotes this function to zip two TreeZippers, applying the function lock-step over both zippers in all directions. - * The structure of the resulting TreeZipper is the structural intersection of the two TreeZippers. - * - * @return A function that zips two TreeZippers with this function. - */ - public final F2W, TreeZipper, TreeZipper> zipTreeZipperM() { - return lift(F2Functions.zipTreeZipperM(this)); - } - - public final F2W contramapFirst(F f) { - return lift(F2Functions.contramapFirst(this, f)); - } - - public final F2W contramapSecond(F f) { - return lift(F2Functions.contramapSecond(this, f)); - } - - public final F2W contramap(F f, F g) { - return lift(F2Functions.contramap(this, f, g)); - } - - public final F2W map(F f) { - return lift(F2Functions.map(this, f)); - } - - - public static class F2WFunc extends F2W { - final F2 func; - public F2WFunc(F2 f) { - func = f; - } - - @Override - public final C f(A a, B b) { - return func.f(a, b); - } - } - - /** - * Lifts the function into the fully featured function wrapper - */ - public static F2W lift(final F2 f) { - return new F2WFunc<>(f); - } - - - -} diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index 490dc9d6..96d24b82 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -1,7 +1,5 @@ package fj; -import static fj.F1Functions.dimap; - import fj.data.*; import static fj.Function.*; @@ -146,7 +144,7 @@ public B append(B a1, B a2) { @Override public F prepend(B b) { - return dimap(def.prepend(g.f(b)), g, f); + return def.prepend(g.f(b)).dimap(g, f); } @Override diff --git a/core/src/main/java/fj/P1.java b/core/src/main/java/fj/P1.java index 5e627403..420b820b 100644 --- a/core/src/main/java/fj/P1.java +++ b/core/src/main/java/fj/P1.java @@ -108,7 +108,7 @@ public final P1 bind(final P1 cb, final F> f) { * Binds the given function to the values in the given P1s with a final join. */ public final P1 bind(final P1 cb, final F2 f) { - return bind(cb, F2W.lift(f).curry()); + return bind(cb, f.curry()); } /** diff --git a/core/src/main/java/fj/P2.java b/core/src/main/java/fj/P2.java index b434a0da..841b356e 100644 --- a/core/src/main/java/fj/P2.java +++ b/core/src/main/java/fj/P2.java @@ -166,7 +166,7 @@ public final Stream sequenceW(final Stream, C>> fs) { * @return the 1-product projection over the first element. */ public final P1 _1_() { - return F1Functions.lazy(P2.__1()).f(this); + return P2.__1().lazy().f(this); } /** @@ -175,7 +175,7 @@ public final P1 _1_() { * @return the 1-product projection over the second element. */ public final P1 _2_() { - return F1Functions.lazy(P2.__2()).f(this); + return P2.__2().lazy().f(this); } /** diff --git a/core/src/main/java/fj/P3.java b/core/src/main/java/fj/P3.java index fe980059..6e08a0ba 100644 --- a/core/src/main/java/fj/P3.java +++ b/core/src/main/java/fj/P3.java @@ -101,7 +101,7 @@ public X _3() { * @return the 1-product projection over the first element. */ public final P1 _1_() { - return F1Functions.lazy(P3.__1()).f(this); + return P3.__1().lazy().f(this); } /** @@ -110,7 +110,7 @@ public final P1 _1_() { * @return the 1-product projection over the second element. */ public final P1 _2_() { - return F1Functions.lazy(P3.__2()).f(this); + return P3.__2().lazy().f(this); } /** @@ -119,7 +119,7 @@ public final P1 _2_() { * @return the 1-product projection over the third element. */ public final P1 _3_() { - return F1Functions.lazy(P3.__3()).f(this); + return P3.__3().lazy().f(this); } diff --git a/core/src/main/java/fj/P4.java b/core/src/main/java/fj/P4.java index a7bf8167..4fa2ef8c 100644 --- a/core/src/main/java/fj/P4.java +++ b/core/src/main/java/fj/P4.java @@ -146,7 +146,7 @@ public X _4() { * @return the 1-product projection over the first element. */ public final P1 _1_() { - return F1Functions.lazy(P4.__1()).f(this); + return P4.__1().lazy().f(this); } /** @@ -155,7 +155,7 @@ public final P1 _1_() { * @return the 1-product projection over the second element. */ public final P1 _2_() { - return F1Functions.lazy(P4.__2()).f(this); + return P4.__2().lazy().f(this); } /** @@ -164,7 +164,7 @@ public final P1 _2_() { * @return the 1-product projection over the third element. */ public final P1 _3_() { - return F1Functions.lazy(P4.__3()).f(this); + return P4.__3().lazy().f(this); } /** @@ -173,7 +173,7 @@ public final P1 _3_() { * @return the 1-product projection over the fourth element. */ public final P1 _4_() { - return F1Functions.lazy(P4.__4()).f(this); + return P4.__4().lazy().f(this); } diff --git a/core/src/main/java/fj/P5.java b/core/src/main/java/fj/P5.java index 69262327..fabc4227 100644 --- a/core/src/main/java/fj/P5.java +++ b/core/src/main/java/fj/P5.java @@ -199,7 +199,7 @@ public X _5() { * @return the 1-product projection over the first element. */ public final P1 _1_() { - return F1Functions.lazy(P5.__1()).f(this); + return P5.__1().lazy().f(this); } /** @@ -208,7 +208,7 @@ public final P1 _1_() { * @return the 1-product projection over the second element. */ public final P1 _2_() { - return F1Functions.lazy(P5.__2()).f(this); + return P5.__2().lazy().f(this); } /** @@ -217,7 +217,7 @@ public final P1 _2_() { * @return the 1-product projection over the third element. */ public final P1 _3_() { - return F1Functions.lazy(P5.__3()).f(this); + return P5.__3().lazy().f(this); } /** @@ -226,7 +226,7 @@ public final P1 _3_() { * @return the 1-product projection over the fourth element. */ public final P1 _4_() { - return F1Functions.lazy(P5.__4()).f(this); + return P5.__4().lazy().f(this); } /** @@ -235,7 +235,7 @@ public final P1 _4_() { * @return the 1-product projection over the fifth element. */ public final P1 _5_() { - return F1Functions.lazy(P5.__5()).f(this); + return P5.__5().lazy().f(this); } /** diff --git a/core/src/main/java/fj/P6.java b/core/src/main/java/fj/P6.java index fbf91948..670622c7 100644 --- a/core/src/main/java/fj/P6.java +++ b/core/src/main/java/fj/P6.java @@ -261,7 +261,7 @@ public X _6() { * @return the 1-product projection over the first element. */ public final P1 _1_() { - return F1Functions.lazy(P6.__1()).f(this); + return P6.__1().lazy().f(this); } /** @@ -270,7 +270,7 @@ public final P1 _1_() { * @return the 1-product projection over the second element. */ public final P1 _2_() { - return F1Functions.lazy(P6.__2()).f(this); + return P6.__2().lazy().f(this); } /** @@ -279,7 +279,7 @@ public final P1 _2_() { * @return the 1-product projection over the third element. */ public final P1 _3_() { - return F1Functions.lazy(P6.__3()).f(this); + return P6.__3().lazy().f(this); } /** @@ -288,7 +288,7 @@ public final P1 _3_() { * @return the 1-product projection over the fourth element. */ public final P1 _4_() { - return F1Functions.lazy(P6.__4()).f(this); + return P6.__4().lazy().f(this); } /** @@ -297,7 +297,7 @@ public final P1 _4_() { * @return the 1-product projection over the fifth element. */ public final P1 _5_() { - return F1Functions.lazy(P6.__5()).f(this); + return P6.__5().lazy().f(this); } /** @@ -306,7 +306,7 @@ public final P1 _5_() { * @return the 1-product projection over the sixth element. */ public final P1 _6_() { - return F1Functions.lazy(P6.__6()).f(this); + return P6.__6().lazy().f(this); } /** diff --git a/core/src/main/java/fj/P7.java b/core/src/main/java/fj/P7.java index b1c67b30..6c9ac804 100644 --- a/core/src/main/java/fj/P7.java +++ b/core/src/main/java/fj/P7.java @@ -330,7 +330,7 @@ public X _7() { * @return the 1-product projection over the first element. */ public final P1 _1_() { - return F1Functions.lazy(P7.__1()).f(this); + return P7.__1().lazy().f(this); } /** @@ -339,7 +339,7 @@ public final P1 _1_() { * @return the 1-product projection over the second element. */ public final P1 _2_() { - return F1Functions.lazy(P7.__2()).f(this); + return P7.__2().lazy().f(this); } /** @@ -348,7 +348,7 @@ public final P1 _2_() { * @return the 1-product projection over the third element. */ public final P1 _3_() { - return F1Functions.lazy(P7.__3()).f(this); + return P7.__3().lazy().f(this); } /** @@ -357,7 +357,7 @@ public final P1 _3_() { * @return the 1-product projection over the fourth element. */ public final P1 _4_() { - return F1Functions.lazy(P7.__4()).f(this); + return P7.__4().lazy().f(this); } /** @@ -366,7 +366,7 @@ public final P1 _4_() { * @return the 1-product projection over the fifth element. */ public final P1 _5_() { - return F1Functions.lazy(P7.__5()).f(this); + return P7.__5().lazy().f(this); } /** @@ -375,7 +375,7 @@ public final P1 _5_() { * @return the 1-product projection over the sixth element. */ public final P1 _6_() { - return F1Functions.lazy(P7.__6()).f(this); + return P7.__6().lazy().f(this); } /** @@ -384,7 +384,7 @@ public final P1 _6_() { * @return the 1-product projection over the seventh element. */ public final P1 _7_() { - return F1Functions.lazy(P7.__7()).f(this); + return P7.__7().lazy().f(this); } /** diff --git a/core/src/main/java/fj/P8.java b/core/src/main/java/fj/P8.java index f15d63a1..f3902c5e 100644 --- a/core/src/main/java/fj/P8.java +++ b/core/src/main/java/fj/P8.java @@ -408,7 +408,7 @@ public X _8() { * @return the 1-product projection over the first element. */ public final P1 _1_() { - return F1Functions.lazy(P8.__1()).f(this); + return P8.__1().lazy().f(this); } /** @@ -417,7 +417,7 @@ public final P1 _1_() { * @return the 1-product projection over the second element. */ public final P1 _2_() { - return F1Functions.lazy(P8.__2()).f(this); + return P8.__2().lazy().f(this); } /** @@ -426,7 +426,7 @@ public final P1 _2_() { * @return the 1-product projection over the third element. */ public final P1 _3_() { - return F1Functions.lazy(P8.__3()).f(this); + return P8.__3().lazy().f(this); } /** @@ -435,7 +435,7 @@ public final P1 _3_() { * @return the 1-product projection over the fourth element. */ public final P1 _4_() { - return F1Functions.lazy(P8.__4()).f(this); + return P8.__4().lazy().f(this); } /** @@ -444,7 +444,7 @@ public final P1 _4_() { * @return the 1-product projection over the fifth element. */ public final P1 _5_() { - return F1Functions.lazy(P8.__5()).f(this); + return P8.__5().lazy().f(this); } /** @@ -453,7 +453,7 @@ public final P1 _5_() { * @return the 1-product projection over the sixth element. */ public final P1 _6_() { - return F1Functions.lazy(P8.__6()).f(this); + return P8.__6().lazy().f(this); } /** @@ -462,7 +462,7 @@ public final P1 _6_() { * @return the 1-product projection over the seventh element. */ public final P1 _7_() { - return F1Functions.lazy(P8.__7()).f(this); + return P8.__7().lazy().f(this); } /** @@ -471,7 +471,7 @@ public final P1 _7_() { * @return the 1-product projection over the eighth element. */ public final P1 _8_() { - return F1Functions.lazy(P8.__8()).f(this); + return P8.__8().lazy().f(this); } /** diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index ef8029b3..6fbc5fc6 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -13,13 +13,10 @@ import java.math.BigDecimal; import java.math.BigInteger; -import static fj.F1Functions.dimap; import static fj.Function.constant; import static fj.Function.identity; import static fj.Monoid.*; import static fj.data.DList.listDList; -import static fj.data.Option.none; -import static fj.data.Option.some; /** * Implementations must satisfy the law of associativity: @@ -193,7 +190,7 @@ public B append(B a1, B a2) { @Override public F prepend(B b) { - return dimap(def.prepend(g.f(b)), g, f); + return def.prepend(g.f(b)).dimap(g, f); } @Override diff --git a/core/src/main/java/fj/control/Trampoline.java b/core/src/main/java/fj/control/Trampoline.java index 47fd282b..e3b58e1c 100644 --- a/core/src/main/java/fj/control/Trampoline.java +++ b/core/src/main/java/fj/control/Trampoline.java @@ -171,7 +171,7 @@ public static F>, Trampoline> suspend_() { * @return A new trampoline that runs this trampoline, then applies the given function to the result. */ public final Trampoline map(final F f) { - return bind(F1Functions.o(Trampoline.pure(), f)); + return bind(Trampoline.pure().o(f)); } /** @@ -266,10 +266,11 @@ public final Trampoline zipWith(final Trampoline b, final F2>, B> eb = b.resume(); for (final P1> x : ea.left()) { for (final P1> y : eb.left()) { - return suspend(x.bind(y, F2Functions.curry((ta, tb) -> suspend(() -> ta.zipWith(tb, f))))); + F, F, Trampoline>> z = ta -> tb -> suspend(() -> ta.zipWith(tb, f)); + return suspend(x.bind(y, z)); } for (final B y : eb.right()) { - return suspend(x.map(ta -> ta.map(F2Functions.f(F2Functions.flip(f), y)))); + return suspend(x.map(ta -> ta.map(f.flip().f(y)))); } } for (final A x : ea.right()) { @@ -277,7 +278,7 @@ public final Trampoline zipWith(final Trampoline b, final F2 pure(f.f(x, y))); } for (final P1> y : eb.left()) { - return suspend(y.map(liftM2(F2Functions.curry(f)).f(pure(x)))); + return suspend(y.map(liftM2(f.curry()).f(pure(x)))); } } throw Bottom.error("Match error: Trampoline is neither done nor suspended."); diff --git a/core/src/main/java/fj/control/parallel/ParModule.java b/core/src/main/java/fj/control/parallel/ParModule.java index 514988aa..b9eeacbf 100644 --- a/core/src/main/java/fj/control/parallel/ParModule.java +++ b/core/src/main/java/fj/control/parallel/ParModule.java @@ -67,7 +67,7 @@ public F, Promise> promise() { * that can be claimed in the future. */ public F> promise(final F f) { - return F1Functions.promiseK(f, strategy); + return f.promiseK(strategy); } /** @@ -88,7 +88,7 @@ public F, F>> promisePure() { * that can be claimed in the future. */ public F2> promise(final F2 f) { - return P2.untuple(F1Functions.promiseK(F2Functions.tuple(f), strategy)); + return P2.untuple(f.tuple().promiseK(strategy)); } diff --git a/core/src/main/java/fj/data/IOFunctions.java b/core/src/main/java/fj/data/IOFunctions.java index 2ea15dfb..9573deea 100644 --- a/core/src/main/java/fj/data/IOFunctions.java +++ b/core/src/main/java/fj/data/IOFunctions.java @@ -2,7 +2,6 @@ import fj.F; import fj.F0; -import fj.F1Functions; import fj.F2; import fj.Function; import fj.P; @@ -204,7 +203,7 @@ public IO> f(IterV it) { return i; } final Input input = Input.el(s); - final F, IterV>, P1>> cont = F1Functions.lazy(Function.apply(input)); + final F, IterV>, P1>> cont = Function., IterV>apply(input).lazy(); i = i.fold(done, cont)._1(); } return i; @@ -248,7 +247,7 @@ public IO> f(IterV it) { } final Input input = Input.el(buffer); final F, IterV>, P1>> cont = - F1Functions.lazy(Function.apply(input)); + Function., IterV>apply(input).lazy(); i = i.fold(done, cont)._1(); } return i; diff --git a/core/src/main/java/fj/data/Iteratee.java b/core/src/main/java/fj/data/Iteratee.java index bcb03cc0..be68ed02 100644 --- a/core/src/main/java/fj/data/Iteratee.java +++ b/core/src/main/java/fj/data/Iteratee.java @@ -2,7 +2,6 @@ import fj.F; import fj.F0; -import fj.F1Functions; import fj.Function; import fj.P; import fj.P2; @@ -82,7 +81,7 @@ public Z fold(final F>, Z> done, final F, IterV, Option> runCont = new F, Option>() { - final F>, Option> done = F1Functions.andThen(P2.__1(), Option.some_()); + final F>, Option> done = P2.>__1().andThen(Option.some_()); final F, IterV>, Option> cont = Function.constant(Option.none()); @Override diff --git a/core/src/main/java/fj/data/List.java b/core/src/main/java/fj/data/List.java index 422d246b..5b5fff67 100644 --- a/core/src/main/java/fj/data/List.java +++ b/core/src/main/java/fj/data/List.java @@ -969,7 +969,7 @@ public final B foldRight(final F2 f, final B b) { * @return A Trampoline containing the final result after the right-fold reduction. */ public final Trampoline foldRightC(final F2 f, final B b) { - return Trampoline.suspend(() -> isEmpty() ? Trampoline.pure(b) : tail().foldRightC(f, b).map(F2Functions.f(f, head()))); + return Trampoline.suspend(() -> isEmpty() ? Trampoline.pure(b) : tail().foldRightC(f, b).map(f.f(head()))); } /** diff --git a/core/src/main/java/fj/data/NonEmptyList.java b/core/src/main/java/fj/data/NonEmptyList.java index 4073476d..34c8b957 100644 --- a/core/src/main/java/fj/data/NonEmptyList.java +++ b/core/src/main/java/fj/data/NonEmptyList.java @@ -169,9 +169,10 @@ public NonEmptyList bind(final F> f) { * @return a NonEmptyList of the sublists of this list. */ public NonEmptyList> sublists() { + F, Option>> f = s -> NonEmptyList.fromList(Conversions.Stream_List().f(s)); return fromList( somes(toList().toStream().substreams() - .map(F1Functions.o(NonEmptyList::fromList, Conversions.Stream_List())).toList())).some(); + .map(f).toList())).some(); } /** diff --git a/core/src/main/java/fj/data/Reader.java b/core/src/main/java/fj/data/Reader.java index af66c87e..9374e300 100644 --- a/core/src/main/java/fj/data/Reader.java +++ b/core/src/main/java/fj/data/Reader.java @@ -1,7 +1,6 @@ package fj.data; import fj.F; -import fj.F1Functions; /** * The Reader monad (also called the function monad, so equivalent to the idea of F). @@ -32,7 +31,7 @@ public final B f(A a) { } public final Reader map(F f) { - return unit(F1Functions.andThen(function, f)); + return unit(function.andThen(f)); } public final Reader andThen(F f) { diff --git a/core/src/main/java/fj/data/Tree.java b/core/src/main/java/fj/data/Tree.java index 9eebb932..f430d6d2 100644 --- a/core/src/main/java/fj/data/Tree.java +++ b/core/src/main/java/fj/data/Tree.java @@ -127,7 +127,7 @@ public static F, P1>>> subForest_() { public Stream flatten() { final F2, P1>, Stream> squish = new F2, P1>, Stream>() { public Stream f(final Tree t, final P1> xs) { - return cons(t.root(), t.subForest().map(Stream., Stream>foldRight().f(F2Functions.curry(this)).f(xs._1()))); + return cons(t.root(), t.subForest().map(Stream., Stream>foldRight().f(this.curry()).f(xs._1()))); } }; return squish.f(this, P.p(Stream.nil())); @@ -305,7 +305,7 @@ public static Show> show2D(final Show s) { * @return A new tree of the results of applying the given function over this tree and the given tree, position-wise. */ public Tree zipWith(final Tree bs, final F2 f) { - return F2Functions.zipTreeM(f).f(this, bs); + return f.zipTreeM().f(this, bs); } /** diff --git a/core/src/main/java/fj/data/TreeMap.java b/core/src/main/java/fj/data/TreeMap.java index cd111d06..38607dbc 100644 --- a/core/src/main/java/fj/data/TreeMap.java +++ b/core/src/main/java/fj/data/TreeMap.java @@ -2,7 +2,6 @@ import fj.Equal; import fj.F; -import fj.F1Functions; import fj.Hash; import fj.Ord; import fj.P; @@ -308,9 +307,9 @@ public TreeMap update(final K k, final F f, final V v) { * and the optional value is the value associated with the given key if present, otherwise None. */ public P3, Option, Set> split(Ord ord, final K k) { - final F>>, Set> getSome = F1Functions.mapSet(F1Functions.o(Option.fromSome(), P2.__2()), ord); + final F>>, Set> getSome = Option.fromSome().o(P2.>__2()).mapSet(ord); return tree.split(p(k, Option.none())).map1(getSome).map3(getSome) - .map2(F1Functions.o(Option.join(), F1Functions.mapOption(P2.__2()))); + .map2(Option.join().o(P2.>__2().mapOption())); } /** @@ -359,7 +358,7 @@ public P3, Option, TreeMap> splitLookup(final K k) { */ @SuppressWarnings("unchecked") public TreeMap map(final F f) { - final F>, P2>> g = compose(p2 -> p(p2._1(), p2._2()), P2.map2_(F1Functions.mapOption(f))); + final F>, P2>> g = compose(p2 -> p(p2._1(), p2._2()), P2.map2_(f.mapOption())); final F>> coord = flip(P.>p2()).f(Option.none()); final Ord o = tree.ord().contramap(coord); return new TreeMap<>(tree.map(TreeMap.ord(o), g)); diff --git a/core/src/main/java/fj/data/TreeZipper.java b/core/src/main/java/fj/data/TreeZipper.java index 9ffad4f4..545238f3 100644 --- a/core/src/main/java/fj/data/TreeZipper.java +++ b/core/src/main/java/fj/data/TreeZipper.java @@ -1,678 +1,678 @@ -package fj.data; - -import fj.*; -import fj.function.Booleans; - -import java.util.Iterator; - -import static fj.Equal.p3Equal; -import static fj.Equal.p4Equal; -import static fj.Equal.streamEqual; -import static fj.Equal.treeEqual; -import static fj.Function.compose; -import static fj.Function.curry; -import static fj.Function.flip; -import static fj.Function.uncurryF2; -import static fj.Show.p3Show; -import static fj.Show.p4Show; -import static fj.Show.streamShow; -import static fj.Show.treeShow; -import static fj.data.Option.none; -import static fj.data.Option.some; -import static fj.data.Stream.nil; -import static fj.data.Stream.unfold; -import static fj.data.Tree.node; -import static fj.data.Tree.unfoldTree; - -/** - * Provides a zipper structure for rose trees, which is a Tree supplied with a location within that tree. - * Provides navigation, insertion, deletion, and memorization of visited locations within a tree. - */ -public final class TreeZipper implements Iterable> { - - /** - * Returns an iterator of all the positions of this TreeZipper. Exists for use with the foreach syntax. - * - * @return An iterator of all the positions of this TreeZipper. - */ - public Iterator> iterator() { - return positions().toTree().iterator(); - } - - private final Tree tree; - private final Stream> lefts; - private final Stream> rights; - private final Stream>, A, Stream>>> parents; - - private TreeZipper(final Tree tree, - final Stream> lefts, - final Stream> rights, - final Stream>, A, Stream>>> parents) { - this.tree = tree; - this.lefts = lefts; - this.rights = rights; - this.parents = parents; - } - - @Override - public final boolean equals(Object other) { - return Equal.equals0(TreeZipper.class, this, other, () -> Equal.treeZipperEqual(Equal.anyEqual())); - } - - @Override - public final int hashCode() { - return Hash.treeZipperHash(Hash.anyHash()).hash(this); - } - - /** - * Creates a new tree zipper given a currently selected tree, a forest on the left, a forest on the right, - * and a stream of parent contexts. - * - * @param tree The currently selected tree. - * @param lefts The selected tree's left siblings, closest first. - * @param rights The selected tree's right siblings, closest first. - * @param parents The parent of the selected tree, and the parent's siblings. - * @return A new zipper with the given tree selected, and the given forests on the left and right. - */ - public static TreeZipper treeZipper(final Tree tree, - final Stream> lefts, - final Stream> rights, - final Stream>, A, Stream>>> parents) { - return new TreeZipper<>(tree, lefts, rights, parents); - } - - /** - * First-class constructor for tree zippers. - * - * @return A function that returns a new tree zipper, given a selected tree, left and right siblings, - * and a parent context. - */ - public static - F, F>, F>, F>, A, Stream>>>, TreeZipper>>>> - treeZipper() { - return curry( - TreeZipper::treeZipper); - } - - /** - * Returns the product-4 representation of this zipper. - * - * @return the product-4 representation of this zipper. - */ - public P4, Stream>, Stream>, Stream>, A, Stream>>>> p() { - return P.p(tree, lefts, rights, parents); - } - - /** - * A first-class function that returns the product-4 representation of a given zipper. - * - * @return a function that converts a given zipper to its product-4 representation. - */ - public static - F, P4, Stream>, Stream>, Stream>, A, Stream>>>>> p_() { - return TreeZipper::p; - } - - /** - * An Equal instance for tree zippers. - * - * @param e An Equal instance for tree elements. - * @return An Equal instance for tree zippers. - */ - public static Equal> eq(final Equal e) { - return p4Equal( - treeEqual(e), - streamEqual(treeEqual(e)), - streamEqual(treeEqual(e)), - streamEqual(p3Equal(streamEqual(treeEqual(e)), e, streamEqual(treeEqual(e))))).contramap(TreeZipper.p_()); - } - - /** - * A Show instance for tree zippers. - * - * @param s A Show instance for tree elements. - * @return A Show instance for tree zippers. - */ - public static Show> show(final Show s) { - return p4Show( - treeShow(s), - streamShow(treeShow(s)), - streamShow(treeShow(s)), - streamShow(p3Show(streamShow(treeShow(s)), s, streamShow(treeShow(s))))).contramap(TreeZipper.p_()); - } - - private static Stream> combChildren(final Stream> ls, - final Tree t, - final Stream> rs) { - return ls.foldLeft(compose(flip(Stream.cons()), P.p1()), Stream.cons(t, P.p(rs))); - } - - /** - * Navigates to the parent of the current location. - * - * @return A new tree zipper focused on the parent node of the current node, - * or none if the current node is the root node. - */ - public Option> parent() { - if (parents.isEmpty()) - return none(); - else { - final P3>, A, Stream>> p = parents.head(); - return some(treeZipper(node(p._2(), combChildren(lefts, tree, rights)), p._1(), p._3(), parents.tail()._1())); - } - } - - /** - * Navigates to the top-most parent of the current location. - * - * @return A new tree zipper focused on the top-most parent of the current node. - */ - public TreeZipper root() { - return parent().option(this, TreeZipper.root_()); - } - - /** - * A first-class version of the root function. - * - * @return A function that returns a new tree-zipper focused on the root of the given tree zipper's tree. - */ - public static F, TreeZipper> root_() { - return TreeZipper::root; - } - - /** - * Navigates to the left sibling of the current location. - * - * @return A new tree zipper focused on the left sibling of the current node, - * or none if there are no siblings on the left. - */ - public Option> left() { - return lefts.isEmpty() ? Option.none() - : some(treeZipper(lefts.head(), lefts.tail()._1(), rights.cons(tree), parents)); - } - - /** - * Navigates to the right sibling of the current location. - * - * @return A new tree zipper focused on the right sibling of the current node, - * or none if there are no siblings on the right. - */ - public Option> right() { - return rights.isEmpty() ? Option.none() - : some(treeZipper(rights.head(), lefts.cons(tree), rights.tail()._1(), parents)); - } - - /** - * Navigtes to the first child of the current location. - * - * @return A new tree zipper focused on the first child of the current node, or none if the node has no children. - */ - public Option> firstChild() { - final Stream> ts = tree.subForest()._1(); - return ts.isEmpty() ? Option.none() - : some(treeZipper(ts.head(), Stream.nil(), ts.tail()._1(), downParents())); - } - - /** - * Navigtes to the last child of the current location. - * - * @return A new tree zipper focused on the last child of the current node, or none if the node has no children. - */ - public Option> lastChild() { - final Stream> ts = tree.subForest()._1().reverse(); - return ts.isEmpty() ? Option.none() - : some(treeZipper(ts.head(), ts.tail()._1(), Stream.nil(), downParents())); - } - - /** - * Navigates to the given child of the current location, starting at index 0. - * - * @param n The index of the child to which to navigate. - * @return An optional tree zipper focused on the child node at the given index, or none if there is no such child. - */ - public Option> getChild(final int n) { - Option> r = none(); - for (final P2>, Stream>> lr - : splitChildren(Stream.nil(), tree.subForest()._1(), n)) { - r = some(treeZipper(lr._1().head(), lr._1().tail()._1(), lr._2(), downParents())); - } - return r; - } - - /** - * Navigates to the first child of the current location, that satisfies the given predicate. - * - * @param p A predicate to be satisfied by the child node. - * @return An optional tree zipper focused on the first child node that satisfies the given predicate, - * or none if there is no such child. - */ - public Option> findChild(final F, Boolean> p) { - Option> r = none(); - - final F2>, Stream>, Option>, Tree, Stream>>>> split = - new F2>, Stream>, Option>, Tree, Stream>>>>() { - public Option>, Tree, Stream>>> f(final Stream> acc, - final Stream> xs) { - return xs.isNotEmpty() - ? p.f(xs.head()) ? some(P.p(acc, xs.head(), xs.tail()._1())) - : f(acc.cons(xs.head()), xs.tail()._1()) - : Option.none(); - } - }; - - Stream> subforest = tree.subForest()._1(); - if (subforest.isNotEmpty()) { - for (final P3>, Tree, Stream>> ltr - : split.f(Stream.nil(), subforest)) { - r = some(treeZipper(ltr._2(), ltr._1(), ltr._3(), downParents())); - } - } - return r; - } - - private Stream>, A, Stream>>> downParents() { - return parents.cons(P.p(lefts, tree.root(), rights)); - } - - private static Option, Stream>> splitChildren(final Stream acc, - final Stream xs, - final int n) { - return n == 0 ? some(P.p(acc, xs)) - : xs.isNotEmpty() ? splitChildren(acc.cons(xs.head()), xs.tail()._1(), n - 1) - : Option.none(); - } - - private static Stream>, A, Stream>>> lp3nil() { - return nil(); - } - - /** - * Creates a new tree zipper focused on the root of the given tree. - * - * @param t A tree over which to create a new zipper. - * @return a new tree zipper focused on the root of the given tree. - */ - public static TreeZipper fromTree(final Tree t) { - return treeZipper(t, Stream.nil(), Stream.nil(), TreeZipper.lp3nil()); - } - - /** - * Creates a new tree zipper focused on the first element of the given forest. - * - * @param ts A forest over which to create a new zipper. - * @return a new tree zipper focused on the first element of the given forest. - */ - public static Option> fromForest(final Stream> ts) { - return ts.isNotEmpty() - ? some(treeZipper(ts.head(), Stream.nil(), ts.tail()._1(), TreeZipper.lp3nil())) - : Option.none(); - } - - /** - * Returns the tree containing this location. - * - * @return the tree containing this location. - */ - public Tree toTree() { - return root().tree; - } - - /** - * Returns the forest containing this location. - * - * @return the forest containing this location. - */ - public Stream> toForest() { - final TreeZipper r = root(); - return combChildren(r.lefts, r.tree, r.rights); - } - - /** - * Returns the tree at the currently focused node. - * - * @return the tree at the currently focused node. - */ - public Tree focus() { - return tree; - } - - /** - * Returns the left siblings of the currently focused node. - * - * @return the left siblings of the currently focused node. - */ - public Stream> lefts() { - return lefts; - } - - /** - * Returns the right siblings of the currently focused node. - * - * @return the right siblings of the currently focused node. - */ - public Stream> rights() { - return rights; - } - - /** - * Returns the parents of the currently focused node. - * - * @return the parents of the currently focused node. - */ - public Stream>, A, Stream>>> parents() { - return parents; - } - - /** - * Indicates whether the current node is at the top of the tree. - * - * @return true if the current node is the root of the tree, otherwise false. - */ - public boolean isRoot() { - return parents.isEmpty(); - } - - /** - * Indicates whether the current node is the leftmost tree in the current forest. - * - * @return true if the current node has no left siblings, otherwise false. - */ - public boolean isFirst() { - return lefts.isEmpty(); - } - - /** - * Indicates whether the current node is the rightmost tree in the current forest. - * - * @return true if the current node has no siblings on its right, otherwise false. - */ - public boolean isLast() { - return rights.isEmpty(); - } - - /** - * Indicates whether the current node is at the bottom of the tree. - * - * @return true if the current node has no child nodes, otherwise false. - */ - public boolean isLeaf() { - return tree.subForest()._1().isEmpty(); - } - - /** - * Indicates whether the current node is a child node of another node. - * - * @return true if the current node has a parent node, otherwise false. - */ - public boolean isChild() { - return !isRoot(); - } - - /** - * Indicates whether the current node has any child nodes. - * - * @return true if the current node has child nodes, otherwise false. - */ - public boolean hasChildren() { - return !isLeaf(); - } - - /** - * Replaces the current node with the given tree. - * - * @param t A tree with which to replace the current node. - * @return A new tree zipper in which the focused node is replaced with the given tree. - */ - public TreeZipper setTree(final Tree t) { - return treeZipper(t, lefts, rights, parents); - } - - /** - * Modifies the current node with the given function. - * - * @param f A function with which to modify the current tree. - * @return A new tree zipper in which the focused node has been transformed by the given function. - */ - public TreeZipper modifyTree(final F, Tree> f) { - return setTree(f.f(tree)); - } - - /** - * Modifies the label at the current node with the given function. - * - * @param f A function with which to transform the current node's label. - * @return A new tree zipper with the focused node's label transformed by the given function. - */ - public TreeZipper modifyLabel(final F f) { - return setLabel(f.f(getLabel())); - } - - /** - * Replaces the label of the current node with the given value. - * - * @param v The new value for the node's label. - * @return A new tree zipper with the focused node's label replaced by the given value. - */ - public TreeZipper setLabel(final A v) { - return modifyTree(t -> Tree.node(v, t.subForest())); - } - - /** - * Returns the label at the current node. - * - * @return the label at the current node. - */ - public A getLabel() { - return tree.root(); - } - - /** - * Inserts a tree to the left of the current position. The inserted tree becomes the current tree. - * - * @param t A tree to insert to the left of the current position. - * @return A new tree zipper with the given tree in focus and the current tree on the right. - */ - public TreeZipper insertLeft(final Tree t) { - return treeZipper(t, lefts, rights.cons(tree), parents); - } - - /** - * Inserts a tree to the right of the current position. The inserted tree becomes the current tree. - * - * @param t A tree to insert to the right of the current position. - * @return A new tree zipper with the given tree in focus and the current tree on the left. - */ - public TreeZipper insertRight(final Tree t) { - return treeZipper(t, lefts.cons(tree), rights, parents); - } - - /** - * Inserts a tree as the first child of the current node. The inserted tree becomes the current tree. - * - * @param t A tree to insert. - * @return A new tree zipper with the given tree in focus, as the first child of the current node. - */ - public TreeZipper insertDownFirst(final Tree t) { - return treeZipper(t, Stream.nil(), tree.subForest()._1(), downParents()); - } - - /** - * Inserts a tree as the last child of the current node. The inserted tree becomes the current tree. - * - * @param t A tree to insert. - * @return A new tree zipper with the given tree in focus, as the last child of the current node. - */ - public TreeZipper insertDownLast(final Tree t) { - return treeZipper(t, tree.subForest()._1().reverse(), Stream.nil(), downParents()); - } - - /** - * Inserts a tree at the specified location in the current node's stream of children. The inserted tree - * becomes the current node. - * - * @param n The index at which to insert the given tree, starting at 0. - * @param t A tree to insert. - * @return A new tree zipper with the given tree in focus, at the specified index in the current node's stream - * of children, or None if the current node has fewer than n children. - */ - public Option> insertDownAt(final int n, final Tree t) { - Option> r = none(); - for (final P2>, Stream>> lr - : splitChildren(Stream.nil(), tree.subForest()._1(), n)) { - r = some(treeZipper(t, lr._1(), lr._2(), downParents())); - } - return r; - } - - /** - * Removes the current node from the tree. The new position becomes the right sibling, or the left sibling - * if the current node has no right siblings, or the parent node if the current node has no siblings. - * - * @return A new tree zipper with the current node removed. - */ - public Option> delete() { - Option> r = none(); - if (rights.isNotEmpty()) - r = some(treeZipper(rights.head(), lefts, rights.tail()._1(), parents)); - else if (lefts.isNotEmpty()) - r = some(treeZipper(lefts.head(), lefts.tail()._1(), rights, parents)); - else for (final TreeZipper loc : parent()) - r = some(loc.modifyTree(t -> node(t.root(), Stream.nil()))); - return r; - } - - /** - * Zips the nodes in this zipper with a boolean that indicates whether that node has focus. - * All of the booleans will be false, except for the focused node. - * - * @return A new zipper of pairs, with each node of this zipper paired with a boolean that is true if that - * node has focus, and false otherwise. - */ - public TreeZipper> zipWithFocus() { - final F> f = flip(P.p2()).f(false); - return map(f).modifyLabel(P2.map2_(Booleans.not)); - } - - /** - * Maps the given function across this zipper (covariant functor pattern). - * - * @param f A function to map across this zipper. - * @return A new zipper with the given function applied to the label of every node. - */ - public TreeZipper map(final F f) { - final F, Tree> g = Tree.fmap_().f(f); - final F>, Stream>> h = Stream., Tree>map_().f(g); - return treeZipper(tree.fmap(f), lefts.map(g), rights.map(g), parents.map( - p -> p.map1(h).map2(f).map3(h))); - } - - /** - * First-class conversion of a Tree to the corresponding tree zipper. - * - * @return A function that takes a tree to its tree zipper representation. - */ - public static F, TreeZipper> fromTree() { - return TreeZipper::fromTree; - } - - /** - * A first-class version of the left() function. - * - * @return A function that focuses the given tree zipper on its left sibling. - */ - public static F, Option>> left_() { - return TreeZipper::left; - } - - /** - * A first-class version of the right() function. - * - * @return A function that focuses the given tree zipper on its right sibling. - */ - public static F, Option>> right_() { - return TreeZipper::right; - } - - /** - * Returns a zipper over the tree of all possible permutations of this tree zipper (comonad pattern). - * This tree zipper becomes the focused node of the new zipper. - * - * @return A tree zipper over the tree of all possible permutations of this tree zipper. - */ - public TreeZipper> positions() { - final Tree> t = unfoldTree(TreeZipper.dwn()).f(this); - final Stream>> l = uf(TreeZipper.left_()); - final Stream>> r = uf(TreeZipper.right_()); - final Stream>>, TreeZipper, Stream>>>> p = unfold( - o -> { - Option>>, TreeZipper, Stream>>>, - Option>>> r1 = none(); - for (final TreeZipper z : o) { - r1 = some(P.p(P.p(z.uf(TreeZipper.left_()), z, z.uf(TreeZipper.right_())), z.parent())); - } - return r1; - }, parent()); - return treeZipper(t, l, r, p); - } - - private Stream>> uf(final F, Option>> f) { - return unfold( - o -> { - Option>, Option>>> r = none(); - for (final TreeZipper c : o) { - r = some(P.p(unfoldTree(TreeZipper.dwn()).f(c), f.f(c))); - } - return r; - }, f.f(this)); - } - - private static F, P2, P1>>>> dwn() { - F>, Option, Option>>>> fwd = o -> o.map(c -> P.p(c, c.right())); - return tz -> P.p(tz, P.lazy(() -> unfold(fwd, tz.firstChild()))); - } - - /** - * Maps the given function over the tree of all positions for this zipper (comonad pattern). Returns a zipper - * over the tree of results of the function application. - * - * @param f A function to map over the tree of all positions for this zipper. - * @return A zipper over the tree of results of the function application. - */ - public TreeZipper cobind(final F, B> f) { - return positions().map(f); - } - - /** - * A first-class version of the findChild function. - * - * @return a function that finds the first child, of a given tree zipper, that matches a given predicate. - */ - public static F2, Boolean>, TreeZipper, Option>> findChild() { - return (f, az) -> az.findChild(f); - } - - /** - * Zips this TreeZipper with another, applying the given function lock-step over both zippers in all directions. - * The structure of the resulting TreeZipper is the structural intersection of the two TreeZippers. - * - * @param bs A TreeZipper to zip this one with. - * @param f A function with which to zip together the two TreeZippers. - * @return The result of applying the given function over this TreeZipper and the given TreeZipper, location-wise. - */ - public TreeZipper zipWith(final TreeZipper bs, final F2 f) { - return F2Functions.zipTreeZipperM(f).f(this, bs); - } - - /** - * Zips this TreeZipper with another, applying the given function lock-step over both zippers in all directions. - * The structure of the resulting TreeZipper is the structural intersection of the two TreeZippers. - * - * @param bs A TreeZipper to zip this one with. - * @param f A function with which to zip together the two TreeZippers. - * @return The result of applying the given function over this TreeZipper and the given TreeZipper, location-wise. - */ - public TreeZipper zipWith(final TreeZipper bs, final F> f) { - return zipWith(bs, uncurryF2(f)); - } -} +package fj.data; + +import fj.*; +import fj.function.Booleans; + +import java.util.Iterator; + +import static fj.Equal.p3Equal; +import static fj.Equal.p4Equal; +import static fj.Equal.streamEqual; +import static fj.Equal.treeEqual; +import static fj.Function.compose; +import static fj.Function.curry; +import static fj.Function.flip; +import static fj.Function.uncurryF2; +import static fj.Show.p3Show; +import static fj.Show.p4Show; +import static fj.Show.streamShow; +import static fj.Show.treeShow; +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.data.Stream.nil; +import static fj.data.Stream.unfold; +import static fj.data.Tree.node; +import static fj.data.Tree.unfoldTree; + +/** + * Provides a zipper structure for rose trees, which is a Tree supplied with a location within that tree. + * Provides navigation, insertion, deletion, and memorization of visited locations within a tree. + */ +public final class TreeZipper implements Iterable> { + + /** + * Returns an iterator of all the positions of this TreeZipper. Exists for use with the foreach syntax. + * + * @return An iterator of all the positions of this TreeZipper. + */ + public Iterator> iterator() { + return positions().toTree().iterator(); + } + + private final Tree tree; + private final Stream> lefts; + private final Stream> rights; + private final Stream>, A, Stream>>> parents; + + private TreeZipper(final Tree tree, + final Stream> lefts, + final Stream> rights, + final Stream>, A, Stream>>> parents) { + this.tree = tree; + this.lefts = lefts; + this.rights = rights; + this.parents = parents; + } + + @Override + public final boolean equals(Object other) { + return Equal.equals0(TreeZipper.class, this, other, () -> Equal.treeZipperEqual(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.treeZipperHash(Hash.anyHash()).hash(this); + } + + /** + * Creates a new tree zipper given a currently selected tree, a forest on the left, a forest on the right, + * and a stream of parent contexts. + * + * @param tree The currently selected tree. + * @param lefts The selected tree's left siblings, closest first. + * @param rights The selected tree's right siblings, closest first. + * @param parents The parent of the selected tree, and the parent's siblings. + * @return A new zipper with the given tree selected, and the given forests on the left and right. + */ + public static TreeZipper treeZipper(final Tree tree, + final Stream> lefts, + final Stream> rights, + final Stream>, A, Stream>>> parents) { + return new TreeZipper<>(tree, lefts, rights, parents); + } + + /** + * First-class constructor for tree zippers. + * + * @return A function that returns a new tree zipper, given a selected tree, left and right siblings, + * and a parent context. + */ + public static + F, F>, F>, F>, A, Stream>>>, TreeZipper>>>> + treeZipper() { + return curry( + TreeZipper::treeZipper); + } + + /** + * Returns the product-4 representation of this zipper. + * + * @return the product-4 representation of this zipper. + */ + public P4, Stream>, Stream>, Stream>, A, Stream>>>> p() { + return P.p(tree, lefts, rights, parents); + } + + /** + * A first-class function that returns the product-4 representation of a given zipper. + * + * @return a function that converts a given zipper to its product-4 representation. + */ + public static + F, P4, Stream>, Stream>, Stream>, A, Stream>>>>> p_() { + return TreeZipper::p; + } + + /** + * An Equal instance for tree zippers. + * + * @param e An Equal instance for tree elements. + * @return An Equal instance for tree zippers. + */ + public static Equal> eq(final Equal e) { + return p4Equal( + treeEqual(e), + streamEqual(treeEqual(e)), + streamEqual(treeEqual(e)), + streamEqual(p3Equal(streamEqual(treeEqual(e)), e, streamEqual(treeEqual(e))))).contramap(TreeZipper.p_()); + } + + /** + * A Show instance for tree zippers. + * + * @param s A Show instance for tree elements. + * @return A Show instance for tree zippers. + */ + public static Show> show(final Show s) { + return p4Show( + treeShow(s), + streamShow(treeShow(s)), + streamShow(treeShow(s)), + streamShow(p3Show(streamShow(treeShow(s)), s, streamShow(treeShow(s))))).contramap(TreeZipper.p_()); + } + + private static Stream> combChildren(final Stream> ls, + final Tree t, + final Stream> rs) { + return ls.foldLeft(compose(flip(Stream.cons()), P.p1()), Stream.cons(t, P.p(rs))); + } + + /** + * Navigates to the parent of the current location. + * + * @return A new tree zipper focused on the parent node of the current node, + * or none if the current node is the root node. + */ + public Option> parent() { + if (parents.isEmpty()) + return none(); + else { + final P3>, A, Stream>> p = parents.head(); + return some(treeZipper(node(p._2(), combChildren(lefts, tree, rights)), p._1(), p._3(), parents.tail()._1())); + } + } + + /** + * Navigates to the top-most parent of the current location. + * + * @return A new tree zipper focused on the top-most parent of the current node. + */ + public TreeZipper root() { + return parent().option(this, TreeZipper.root_()); + } + + /** + * A first-class version of the root function. + * + * @return A function that returns a new tree-zipper focused on the root of the given tree zipper's tree. + */ + public static F, TreeZipper> root_() { + return TreeZipper::root; + } + + /** + * Navigates to the left sibling of the current location. + * + * @return A new tree zipper focused on the left sibling of the current node, + * or none if there are no siblings on the left. + */ + public Option> left() { + return lefts.isEmpty() ? Option.none() + : some(treeZipper(lefts.head(), lefts.tail()._1(), rights.cons(tree), parents)); + } + + /** + * Navigates to the right sibling of the current location. + * + * @return A new tree zipper focused on the right sibling of the current node, + * or none if there are no siblings on the right. + */ + public Option> right() { + return rights.isEmpty() ? Option.none() + : some(treeZipper(rights.head(), lefts.cons(tree), rights.tail()._1(), parents)); + } + + /** + * Navigtes to the first child of the current location. + * + * @return A new tree zipper focused on the first child of the current node, or none if the node has no children. + */ + public Option> firstChild() { + final Stream> ts = tree.subForest()._1(); + return ts.isEmpty() ? Option.none() + : some(treeZipper(ts.head(), Stream.nil(), ts.tail()._1(), downParents())); + } + + /** + * Navigtes to the last child of the current location. + * + * @return A new tree zipper focused on the last child of the current node, or none if the node has no children. + */ + public Option> lastChild() { + final Stream> ts = tree.subForest()._1().reverse(); + return ts.isEmpty() ? Option.none() + : some(treeZipper(ts.head(), ts.tail()._1(), Stream.nil(), downParents())); + } + + /** + * Navigates to the given child of the current location, starting at index 0. + * + * @param n The index of the child to which to navigate. + * @return An optional tree zipper focused on the child node at the given index, or none if there is no such child. + */ + public Option> getChild(final int n) { + Option> r = none(); + for (final P2>, Stream>> lr + : splitChildren(Stream.nil(), tree.subForest()._1(), n)) { + r = some(treeZipper(lr._1().head(), lr._1().tail()._1(), lr._2(), downParents())); + } + return r; + } + + /** + * Navigates to the first child of the current location, that satisfies the given predicate. + * + * @param p A predicate to be satisfied by the child node. + * @return An optional tree zipper focused on the first child node that satisfies the given predicate, + * or none if there is no such child. + */ + public Option> findChild(final F, Boolean> p) { + Option> r = none(); + + final F2>, Stream>, Option>, Tree, Stream>>>> split = + new F2>, Stream>, Option>, Tree, Stream>>>>() { + public Option>, Tree, Stream>>> f(final Stream> acc, + final Stream> xs) { + return xs.isNotEmpty() + ? p.f(xs.head()) ? some(P.p(acc, xs.head(), xs.tail()._1())) + : f(acc.cons(xs.head()), xs.tail()._1()) + : Option.none(); + } + }; + + Stream> subforest = tree.subForest()._1(); + if (subforest.isNotEmpty()) { + for (final P3>, Tree, Stream>> ltr + : split.f(Stream.nil(), subforest)) { + r = some(treeZipper(ltr._2(), ltr._1(), ltr._3(), downParents())); + } + } + return r; + } + + private Stream>, A, Stream>>> downParents() { + return parents.cons(P.p(lefts, tree.root(), rights)); + } + + private static Option, Stream>> splitChildren(final Stream acc, + final Stream xs, + final int n) { + return n == 0 ? some(P.p(acc, xs)) + : xs.isNotEmpty() ? splitChildren(acc.cons(xs.head()), xs.tail()._1(), n - 1) + : Option.none(); + } + + private static Stream>, A, Stream>>> lp3nil() { + return nil(); + } + + /** + * Creates a new tree zipper focused on the root of the given tree. + * + * @param t A tree over which to create a new zipper. + * @return a new tree zipper focused on the root of the given tree. + */ + public static TreeZipper fromTree(final Tree t) { + return treeZipper(t, Stream.nil(), Stream.nil(), TreeZipper.lp3nil()); + } + + /** + * Creates a new tree zipper focused on the first element of the given forest. + * + * @param ts A forest over which to create a new zipper. + * @return a new tree zipper focused on the first element of the given forest. + */ + public static Option> fromForest(final Stream> ts) { + return ts.isNotEmpty() + ? some(treeZipper(ts.head(), Stream.nil(), ts.tail()._1(), TreeZipper.lp3nil())) + : Option.none(); + } + + /** + * Returns the tree containing this location. + * + * @return the tree containing this location. + */ + public Tree toTree() { + return root().tree; + } + + /** + * Returns the forest containing this location. + * + * @return the forest containing this location. + */ + public Stream> toForest() { + final TreeZipper r = root(); + return combChildren(r.lefts, r.tree, r.rights); + } + + /** + * Returns the tree at the currently focused node. + * + * @return the tree at the currently focused node. + */ + public Tree focus() { + return tree; + } + + /** + * Returns the left siblings of the currently focused node. + * + * @return the left siblings of the currently focused node. + */ + public Stream> lefts() { + return lefts; + } + + /** + * Returns the right siblings of the currently focused node. + * + * @return the right siblings of the currently focused node. + */ + public Stream> rights() { + return rights; + } + + /** + * Returns the parents of the currently focused node. + * + * @return the parents of the currently focused node. + */ + public Stream>, A, Stream>>> parents() { + return parents; + } + + /** + * Indicates whether the current node is at the top of the tree. + * + * @return true if the current node is the root of the tree, otherwise false. + */ + public boolean isRoot() { + return parents.isEmpty(); + } + + /** + * Indicates whether the current node is the leftmost tree in the current forest. + * + * @return true if the current node has no left siblings, otherwise false. + */ + public boolean isFirst() { + return lefts.isEmpty(); + } + + /** + * Indicates whether the current node is the rightmost tree in the current forest. + * + * @return true if the current node has no siblings on its right, otherwise false. + */ + public boolean isLast() { + return rights.isEmpty(); + } + + /** + * Indicates whether the current node is at the bottom of the tree. + * + * @return true if the current node has no child nodes, otherwise false. + */ + public boolean isLeaf() { + return tree.subForest()._1().isEmpty(); + } + + /** + * Indicates whether the current node is a child node of another node. + * + * @return true if the current node has a parent node, otherwise false. + */ + public boolean isChild() { + return !isRoot(); + } + + /** + * Indicates whether the current node has any child nodes. + * + * @return true if the current node has child nodes, otherwise false. + */ + public boolean hasChildren() { + return !isLeaf(); + } + + /** + * Replaces the current node with the given tree. + * + * @param t A tree with which to replace the current node. + * @return A new tree zipper in which the focused node is replaced with the given tree. + */ + public TreeZipper setTree(final Tree t) { + return treeZipper(t, lefts, rights, parents); + } + + /** + * Modifies the current node with the given function. + * + * @param f A function with which to modify the current tree. + * @return A new tree zipper in which the focused node has been transformed by the given function. + */ + public TreeZipper modifyTree(final F, Tree> f) { + return setTree(f.f(tree)); + } + + /** + * Modifies the label at the current node with the given function. + * + * @param f A function with which to transform the current node's label. + * @return A new tree zipper with the focused node's label transformed by the given function. + */ + public TreeZipper modifyLabel(final F f) { + return setLabel(f.f(getLabel())); + } + + /** + * Replaces the label of the current node with the given value. + * + * @param v The new value for the node's label. + * @return A new tree zipper with the focused node's label replaced by the given value. + */ + public TreeZipper setLabel(final A v) { + return modifyTree(t -> Tree.node(v, t.subForest())); + } + + /** + * Returns the label at the current node. + * + * @return the label at the current node. + */ + public A getLabel() { + return tree.root(); + } + + /** + * Inserts a tree to the left of the current position. The inserted tree becomes the current tree. + * + * @param t A tree to insert to the left of the current position. + * @return A new tree zipper with the given tree in focus and the current tree on the right. + */ + public TreeZipper insertLeft(final Tree t) { + return treeZipper(t, lefts, rights.cons(tree), parents); + } + + /** + * Inserts a tree to the right of the current position. The inserted tree becomes the current tree. + * + * @param t A tree to insert to the right of the current position. + * @return A new tree zipper with the given tree in focus and the current tree on the left. + */ + public TreeZipper insertRight(final Tree t) { + return treeZipper(t, lefts.cons(tree), rights, parents); + } + + /** + * Inserts a tree as the first child of the current node. The inserted tree becomes the current tree. + * + * @param t A tree to insert. + * @return A new tree zipper with the given tree in focus, as the first child of the current node. + */ + public TreeZipper insertDownFirst(final Tree t) { + return treeZipper(t, Stream.nil(), tree.subForest()._1(), downParents()); + } + + /** + * Inserts a tree as the last child of the current node. The inserted tree becomes the current tree. + * + * @param t A tree to insert. + * @return A new tree zipper with the given tree in focus, as the last child of the current node. + */ + public TreeZipper insertDownLast(final Tree t) { + return treeZipper(t, tree.subForest()._1().reverse(), Stream.nil(), downParents()); + } + + /** + * Inserts a tree at the specified location in the current node's stream of children. The inserted tree + * becomes the current node. + * + * @param n The index at which to insert the given tree, starting at 0. + * @param t A tree to insert. + * @return A new tree zipper with the given tree in focus, at the specified index in the current node's stream + * of children, or None if the current node has fewer than n children. + */ + public Option> insertDownAt(final int n, final Tree t) { + Option> r = none(); + for (final P2>, Stream>> lr + : splitChildren(Stream.nil(), tree.subForest()._1(), n)) { + r = some(treeZipper(t, lr._1(), lr._2(), downParents())); + } + return r; + } + + /** + * Removes the current node from the tree. The new position becomes the right sibling, or the left sibling + * if the current node has no right siblings, or the parent node if the current node has no siblings. + * + * @return A new tree zipper with the current node removed. + */ + public Option> delete() { + Option> r = none(); + if (rights.isNotEmpty()) + r = some(treeZipper(rights.head(), lefts, rights.tail()._1(), parents)); + else if (lefts.isNotEmpty()) + r = some(treeZipper(lefts.head(), lefts.tail()._1(), rights, parents)); + else for (final TreeZipper loc : parent()) + r = some(loc.modifyTree(t -> node(t.root(), Stream.nil()))); + return r; + } + + /** + * Zips the nodes in this zipper with a boolean that indicates whether that node has focus. + * All of the booleans will be false, except for the focused node. + * + * @return A new zipper of pairs, with each node of this zipper paired with a boolean that is true if that + * node has focus, and false otherwise. + */ + public TreeZipper> zipWithFocus() { + final F> f = flip(P.p2()).f(false); + return map(f).modifyLabel(P2.map2_(Booleans.not)); + } + + /** + * Maps the given function across this zipper (covariant functor pattern). + * + * @param f A function to map across this zipper. + * @return A new zipper with the given function applied to the label of every node. + */ + public TreeZipper map(final F f) { + final F, Tree> g = Tree.fmap_().f(f); + final F>, Stream>> h = Stream., Tree>map_().f(g); + return treeZipper(tree.fmap(f), lefts.map(g), rights.map(g), parents.map( + p -> p.map1(h).map2(f).map3(h))); + } + + /** + * First-class conversion of a Tree to the corresponding tree zipper. + * + * @return A function that takes a tree to its tree zipper representation. + */ + public static F, TreeZipper> fromTree() { + return TreeZipper::fromTree; + } + + /** + * A first-class version of the left() function. + * + * @return A function that focuses the given tree zipper on its left sibling. + */ + public static F, Option>> left_() { + return TreeZipper::left; + } + + /** + * A first-class version of the right() function. + * + * @return A function that focuses the given tree zipper on its right sibling. + */ + public static F, Option>> right_() { + return TreeZipper::right; + } + + /** + * Returns a zipper over the tree of all possible permutations of this tree zipper (comonad pattern). + * This tree zipper becomes the focused node of the new zipper. + * + * @return A tree zipper over the tree of all possible permutations of this tree zipper. + */ + public TreeZipper> positions() { + final Tree> t = unfoldTree(TreeZipper.dwn()).f(this); + final Stream>> l = uf(TreeZipper.left_()); + final Stream>> r = uf(TreeZipper.right_()); + final Stream>>, TreeZipper, Stream>>>> p = unfold( + o -> { + Option>>, TreeZipper, Stream>>>, + Option>>> r1 = none(); + for (final TreeZipper z : o) { + r1 = some(P.p(P.p(z.uf(TreeZipper.left_()), z, z.uf(TreeZipper.right_())), z.parent())); + } + return r1; + }, parent()); + return treeZipper(t, l, r, p); + } + + private Stream>> uf(final F, Option>> f) { + return unfold( + o -> { + Option>, Option>>> r = none(); + for (final TreeZipper c : o) { + r = some(P.p(unfoldTree(TreeZipper.dwn()).f(c), f.f(c))); + } + return r; + }, f.f(this)); + } + + private static F, P2, P1>>>> dwn() { + F>, Option, Option>>>> fwd = o -> o.map(c -> P.p(c, c.right())); + return tz -> P.p(tz, P.lazy(() -> unfold(fwd, tz.firstChild()))); + } + + /** + * Maps the given function over the tree of all positions for this zipper (comonad pattern). Returns a zipper + * over the tree of results of the function application. + * + * @param f A function to map over the tree of all positions for this zipper. + * @return A zipper over the tree of results of the function application. + */ + public TreeZipper cobind(final F, B> f) { + return positions().map(f); + } + + /** + * A first-class version of the findChild function. + * + * @return a function that finds the first child, of a given tree zipper, that matches a given predicate. + */ + public static F2, Boolean>, TreeZipper, Option>> findChild() { + return (f, az) -> az.findChild(f); + } + + /** + * Zips this TreeZipper with another, applying the given function lock-step over both zippers in all directions. + * The structure of the resulting TreeZipper is the structural intersection of the two TreeZippers. + * + * @param bs A TreeZipper to zip this one with. + * @param f A function with which to zip together the two TreeZippers. + * @return The result of applying the given function over this TreeZipper and the given TreeZipper, location-wise. + */ + public TreeZipper zipWith(final TreeZipper bs, final F2 f) { + return f.zipTreeZipperM().f(this, bs); + } + + /** + * Zips this TreeZipper with another, applying the given function lock-step over both zippers in all directions. + * The structure of the resulting TreeZipper is the structural intersection of the two TreeZippers. + * + * @param bs A TreeZipper to zip this one with. + * @param f A function with which to zip together the two TreeZippers. + * @return The result of applying the given function over this TreeZipper and the given TreeZipper, location-wise. + */ + public TreeZipper zipWith(final TreeZipper bs, final F> f) { + return zipWith(bs, uncurryF2(f)); + } +} diff --git a/core/src/main/java/fj/data/Zipper.java b/core/src/main/java/fj/data/Zipper.java index 2514d8e2..4b96377f 100644 --- a/core/src/main/java/fj/data/Zipper.java +++ b/core/src/main/java/fj/data/Zipper.java @@ -1,585 +1,585 @@ -package fj.data; - -import fj.*; -import fj.function.Integers; - -import java.util.Iterator; - -import static fj.Function.compose; -import static fj.Function.curry; -import static fj.Function.flip; -import static fj.Function.join; -import static fj.Function.uncurryF2; -import static fj.data.Option.none; -import static fj.data.Option.some; -import static fj.data.Stream.nil; -import static fj.data.Stream.repeat; - -/** - * Provides a pointed stream, which is a non-empty zipper-like stream structure that tracks an index (focus) - * position in a stream. Focus can be moved forward and backwards through the stream, elements can be inserted - * before or after the focused position, and the focused item can be deleted. - *

      - * Based on the pointedlist library by Jeff Wheeler. - */ -public final class Zipper implements Iterable> { - private final Stream left; - private final A focus; - private final Stream right; - - private Zipper(final Stream left, final A focus, final Stream right) { - this.left = left; - this.focus = focus; - this.right = right; - } - - - /** - * Creates a new Zipper with the given streams before and after the focus, and the given focused item. - * - * @param left The stream of elements before the focus. - * @param focus The element under focus. - * @param right The stream of elements after the focus. - * @return a new Zipper with the given streams before and after the focus, and the given focused item. - */ - public static Zipper zipper(final Stream left, final A focus, final Stream right) { - return new Zipper<>(left, focus, right); - } - - /** - * Creates a new Zipper from the given triple. - * - * @param p A triple of the elements before the focus, the focus element, and the elements after the focus, - * respectively. - * @return a new Zipper created from the given triple. - */ - public static Zipper zipper(final P3, A, Stream> p) { - return new Zipper<>(p._1(), p._2(), p._3()); - } - - /** - * First-class constructor of zippers. - * - * @return A function that yields a new zipper given streams on the left and right and a focus element. - */ - public static F3, A, Stream, Zipper> zipper() { - return Zipper::zipper; - } - - /** - * Returns the product-3 representation of this Zipper. - * - * @return the product-3 representation of this Zipper. - */ - public P3, A, Stream> p() { - return P.p(left, focus, right); - } - - /** - * A first-class function that yields the product-3 representation of a given Zipper. - * - * @return A first-class function that yields the product-3 representation of a given Zipper. - */ - public static F, P3, A, Stream>> p_() { - return Zipper::p; - } - - /** - * An Ord instance for Zippers. - * - * @param o An Ord instance for the element type. - * @return An Ord instance for Zippers. - */ - public static Ord> ord(final Ord o) { - final Ord> so = Ord.streamOrd(o); - return Ord.p3Ord(so, o, so).contramap(Zipper.p_()); - } - - @Override - public final boolean equals(Object other) { - return Equal.equals0(Zipper.class, this, other, () -> Equal.zipperEqual(Equal.anyEqual())); - } - - @Override - public final int hashCode() { - return Hash.zipperHash(Hash.anyHash()).hash(this); - } - - /** - * An Equal instance for Zippers. - * - * @param e An Equal instance for the element type. - * @return An Equal instance for Zippers. - */ - public static Equal> eq(final Equal e) { - final Equal> se = Equal.streamEqual(e); - return Equal.p3Equal(se, e, se).contramap(Zipper.p_()); - } - - /** - * A Show instance for Zippers. - * - * @param s A Show instance for the element type. - * @return A Show instance for Zippers. - */ - public static Show> show(final Show s) { - final Show> ss = Show.streamShow(s); - return Show.p3Show(ss, s, ss).contramap(Zipper.p_()); - } - - /** - * Maps the given function across the elements of this zipper (covariant functor pattern). - * - * @param f A function to map across this zipper. - * @return A new zipper with the given function applied to all elements. - */ - public Zipper map(final F f) { - return zipper(left.map(f), f.f(focus), right.map(f)); - } - - /** - * Performs a right-fold reduction across this zipper. - * - * @param f The function to apply on each element of this zipper. - * @param z The beginning value to start the application from. - * @return the final result after the right-fold reduction. - */ - public B foldRight(final F> f, final B z) { - return left.foldLeft(flip(f), - right.cons(focus).foldRight(compose( - Function., B, B>andThen().f(P1.__1()), f), z)); - } - - /** - * Creates a new zipper with a single element. - * - * @param a The focus element of the new zipper. - * @return a new zipper with a single element which is in focus. - */ - public static Zipper single(final A a) { - return zipper(Stream.nil(), a, Stream.nil()); - } - - /** - * Possibly create a zipper if the provided stream has at least one element, otherwise None. - * The provided stream's head will be the focus of the zipper, and the rest of the stream will follow - * on the right side. - * - * @param a The stream from which to create a zipper. - * @return a new zipper if the provided stream has at least one element, otherwise None. - */ - @SuppressWarnings("IfMayBeConditional") - public static Option> fromStream(final Stream a) { - if (a.isEmpty()) - return none(); - else - return some(zipper(Stream.nil(), a.head(), a.tail()._1())); - } - - /** - * Possibly create a zipper if the provided stream has at least one element, otherwise None. - * The provided stream's last element will be the focus of the zipper, following the rest of the stream in order, - * to the left. - * - * @param a The stream from which to create a zipper. - * @return a new zipper if the provided stream has at least one element, otherwise None. - */ - public static Option> fromStreamEnd(final Stream a) { - if (a.isEmpty()) - return none(); - else { - final Stream xs = a.reverse(); - return some(zipper(xs.tail()._1(), xs.head(), Stream.nil())); - } - } - - /** - * Returns the focus element of this zipper. - * - * @return the focus element of this zipper. - */ - public A focus() { - return focus; - } - - /** - * Possibly moves the focus to the next element in the list. - * - * @return An optional zipper with the focus moved one element to the right, if there are elements to the right of - * focus, otherwise None. - */ - public Option> next() { - return right.isEmpty() ? Option.none() : some(tryNext()); - } - - /** - * Attempts to move the focus to the next element, or throws an error if there are no more elements. - * - * @return A zipper with the focus moved one element to the right, if there are elements to the right of - * focus, otherwise throws an error. - */ - public Zipper tryNext() { - if (right.isEmpty()) - throw new Error("Tried next at the end of a zipper."); - else - return zipper(left.cons(focus), right.head(), right.tail()._1()); - } - - /** - * Possibly moves the focus to the previous element in the list. - * - * @return An optional zipper with the focus moved one element to the left, if there are elements to the left of - * focus, otherwise None. - */ - public Option> previous() { - return left.isEmpty() ? Option.none() : some(tryPrevious()); - } - - /** - * Attempts to move the focus to the previous element, or throws an error if there are no more elements. - * - * @return A zipper with the focus moved one element to the left, if there are elements to the left of - * focus, otherwise throws an error. - */ - public Zipper tryPrevious() { - if (left.isEmpty()) - throw new Error("Tried previous at the beginning of a zipper."); - else - return zipper(left.tail()._1(), left.head(), right.cons(focus)); - } - - /** - * First-class version of the next() function. - * - * @return A function that moves the given zipper's focus to the next element. - */ - public static F, Option>> next_() { - return Zipper::next; - } - - /** - * First-class version of the previous() function. - * - * @return A function that moves the given zipper's focus to the previous element. - */ - public static F, Option>> previous_() { - return Zipper::previous; - } - - /** - * Inserts an element to the left of the focus, then moves the focus to the new element. - * - * @param a A new element to insert into this zipper. - * @return A new zipper with the given element in focus, and the current focus element on its right. - */ - public Zipper insertLeft(final A a) { - return zipper(left, a, right.cons(focus)); - } - - /** - * Inserts an element to the right of the focus, then moves the focus to the new element. - * - * @param a A new element to insert into this zipper. - * @return A new zipper with the given element in focus, and the current focus element on its left. - */ - public Zipper insertRight(final A a) { - return zipper(left.cons(focus), a, right); - } - - /** - * Possibly deletes the element at the focus, then moves the element on the left into focus. - * If no element is on the left, focus on the element to the right. - * Returns None if the focus element is the only element in this zipper. - * - * @return A new zipper with this zipper's focus element removed, or None if deleting the focus element - * would cause the zipper to be empty. - */ - public Option> deleteLeft() { - return left.isEmpty() && right.isEmpty() - ? Option.none() - : some(zipper(left.isEmpty() ? left : left.tail()._1(), - left.isEmpty() ? right.head() : left.head(), - left.isEmpty() ? right.tail()._1() : right)); - } - - /** - * Possibly deletes the element at the focus, then moves the element on the right into focus. - * If no element is on the right, focus on the element to the left. - * Returns None if the focus element is the only element in this zipper. - * - * @return A new zipper with this zipper's focus element removed, or None if deleting the focus element - * would cause the zipper to be empty. - */ - public Option> deleteRight() { - return left.isEmpty() && right.isEmpty() - ? Option.none() - : some(zipper(right.isEmpty() ? left.tail()._1() : left, - right.isEmpty() ? left.head() : right.head(), - right.isEmpty() ? right : right.tail()._1())); - } - - /** - * Deletes all elements in the zipper except the focus. - * - * @return A new zipper with the focus element as the only element. - */ - public Zipper deleteOthers() { - final Stream nil = nil(); - return zipper(nil, focus, nil); - } - - /** - * Returns the length of this zipper. - * - * @return the length of this zipper. - */ - public int length() { - return foldRight(Function.constant(Integers.add.f(1)), 0); - } - - /** - * Returns whether the focus is on the first element. - * - * @return true if the focus is on the first element, otherwise false. - */ - public boolean atStart() { - return left.isEmpty(); - } - - /** - * Returns whether the focus is on the last element. - * - * @return true if the focus is on the last element, otherwise false. - */ - public boolean atEnd() { - return right.isEmpty(); - } - - /** - * Creates a zipper of variations of this zipper, in which each element is focused, - * with this zipper as the focus of the zipper of zippers (comonad pattern). - * - * @return a zipper of variations of the provided zipper, in which each element is focused, - * with this zipper as the focus of the zipper of zippers. - */ - public Zipper> positions() { - final Stream> left = Stream.unfold( - p -> p.previous().map(join(P.p2())), this); - final Stream> right = Stream.unfold( - p -> p.next().map(join(P.p2())), this); - - return zipper(left, this, right); - } - - /** - * Maps over variations of this zipper, such that the given function is applied to each variation (comonad pattern). - * - * @param f The comonadic function to apply for each variation of this zipper. - * @return A new zipper, with the given function applied for each variation of this zipper. - */ - public Zipper cobind(final F, B> f) { - return positions().map(f); - } - - /** - * Zips the elements of this zipper with a boolean that indicates whether that element has focus. - * All of the booleans will be false, except the focused element. - * - * @return A new zipper of pairs, with each element of this zipper paired with a boolean that is true if that - * element has focus, and false otherwise. - */ - public Zipper> zipWithFocus() { - return zipper(left.zip(repeat(false)), P.p(focus, true), right.zip(repeat(false))); - } - - /** - * Move the focus to the specified index. - * - * @param n The index to which to move the focus. - * @return A new zipper with the focus moved to the specified index, or none if there is no such index. - */ - public Option> move(final int n) { - final int ll = left.length(); - final int rl = right.length(); - Option> p = some(this); - if (n < 0 || n >= length()) - return none(); - else if (ll >= n) - for (int i = ll - n; i > 0; i--) - p = p.bind(Zipper.previous_()); - else if (rl >= n) - for (int i = rl - n; i > 0; i--) - p = p.bind(Zipper.next_()); - return p; - } - - /** - * A first-class version of the move function. - * - * @return A function that moves the focus of the given zipper to the given index. - */ - public static F, Option>>> move() { - return curry((i, a) -> a.move(i)); - } - - /** - * Moves the focus to the element matching the given predicate, if present. - * - * @param p A predicate to match. - * @return A new zipper with the nearest matching element focused if it is present in this zipper. - */ - public Option> find(final F p) { - if (p.f(focus())) - return some(this); - else { - final Zipper> ps = positions(); - return ps.lefts().interleave(ps.rights()).find(zipper -> p.f(zipper.focus())); - } - } - - /** - * Returns the index of the focus. - * - * @return the index of the focus. - */ - public int index() { - return left.length(); - } - - /** - * Move the focus to the next element. If the last element is focused, loop to the first element. - * - * @return A new zipper with the next element focused, unless the last element is currently focused, in which case - * the first element becomes focused. - */ - public Zipper cycleNext() { - if (left.isEmpty() && right.isEmpty()) - return this; - else if (right.isEmpty()) { - final Stream xs = left.reverse(); - return zipper(Stream.nil(), xs.head(), xs.tail()._1().snoc(P.p(focus))); - } else - return tryNext(); - } - - /** - * Move the focus to the previous element. If the first element is focused, loop to the last element. - * - * @return A new zipper with the previous element focused, unless the first element is currently focused, - * in which case the last element becomes focused. - */ - public Zipper cyclePrevious() { - if (left.isEmpty() && right.isEmpty()) - return this; - else if (left.isEmpty()) { - final Stream xs = right.reverse(); - return zipper(xs.tail()._1().snoc(P.p(focus)), xs.head(), Stream.nil()); - } else - return tryPrevious(); - } - - /** - * Possibly deletes the element at the focus, then move the element on the left into focus. If no element is on the - * left, focus on the last element. If the deletion will cause the list to be empty, return None. - * - * @return A new zipper with the focused element removed, and focus on the previous element to the left, or the last - * element if there is no element to the left. - */ - public Option> deleteLeftCycle() { - if (left.isEmpty() && right.isEmpty()) - return none(); - else if (left.isNotEmpty()) - return some(zipper(left.tail()._1(), left.head(), right)); - else { - final Stream xs = right.reverse(); - return some(zipper(xs.tail()._1(), xs.head(), Stream.nil())); - } - } - - /** - * Possibly deletes the element at the focus, then move the element on the right into focus. If no element is on the - * right, focus on the first element. If the deletion will cause the list to be empty, return None. - * - * @return A new zipper with the focused element removed, and focus on the next element to the right, or the first - * element if there is no element to the right. - */ - public Option> deleteRightCycle() { - if (left.isEmpty() && right.isEmpty()) - return none(); - else if (right.isNotEmpty()) - return some(zipper(left, right.head(), right.tail()._1())); - else { - final Stream xs = left.reverse(); - return some(zipper(Stream.nil(), xs.head(), xs.tail()._1())); - } - } - - /** - * Replaces the element in focus with the given element. - * - * @param a An element to replace the focused element with. - * @return A new zipper with the given element in focus. - */ - public Zipper replace(final A a) { - return zipper(left, a, right); - } - - /** - * Returns the Stream representation of this zipper. - * - * @return A stream that contains all the elements of this zipper. - */ - public Stream toStream() { - return left.reverse().snoc(P.p(focus)).append(right); - } - - /** - * Returns a Stream of the elements to the left of focus. - * - * @return a Stream of the elements to the left of focus. - */ - public Stream lefts() { - return left; - } - - /** - * Returns a Stream of the elements to the right of focus. - * - * @return a Stream of the elements to the right of focus. - */ - public Stream rights() { - return right; - } - - /** - * Zips this Zipper with another, applying the given function lock-step over both zippers in both directions. - * The structure of the resulting Zipper is the structural intersection of the two Zippers. - * - * @param bs A Zipper to zip this one with. - * @param f A function with which to zip together the two Zippers. - * @return The result of applying the given function over this Zipper and the given Zipper, location-wise. - */ - public Zipper zipWith(final Zipper bs, final F2 f) { - return F2Functions.zipZipperM(f).f(this, bs); - } - - - /** - * Zips this Zipper with another, applying the given function lock-step over both zippers in both directions. - * The structure of the resulting Zipper is the structural intersection of the two Zippers. - * - * @param bs A Zipper to zip this one with. - * @param f A function with which to zip together the two Zippers. - * @return The result of applying the given function over this Zipper and the given Zipper, location-wise. - */ - public Zipper zipWith(final Zipper bs, final F> f) { - return zipWith(bs, uncurryF2(f)); - } - - /** - * Returns an iterator of all the positions of this Zipper, starting from the leftmost position. - * - * @return An iterator of all the positions of this Zipper, starting from the leftmost position. - */ - public Iterator> iterator() { return positions().toStream().iterator(); } -} +package fj.data; + +import fj.*; +import fj.function.Integers; + +import java.util.Iterator; + +import static fj.Function.compose; +import static fj.Function.curry; +import static fj.Function.flip; +import static fj.Function.join; +import static fj.Function.uncurryF2; +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.data.Stream.nil; +import static fj.data.Stream.repeat; + +/** + * Provides a pointed stream, which is a non-empty zipper-like stream structure that tracks an index (focus) + * position in a stream. Focus can be moved forward and backwards through the stream, elements can be inserted + * before or after the focused position, and the focused item can be deleted. + *

      + * Based on the pointedlist library by Jeff Wheeler. + */ +public final class Zipper implements Iterable> { + private final Stream left; + private final A focus; + private final Stream right; + + private Zipper(final Stream left, final A focus, final Stream right) { + this.left = left; + this.focus = focus; + this.right = right; + } + + + /** + * Creates a new Zipper with the given streams before and after the focus, and the given focused item. + * + * @param left The stream of elements before the focus. + * @param focus The element under focus. + * @param right The stream of elements after the focus. + * @return a new Zipper with the given streams before and after the focus, and the given focused item. + */ + public static Zipper zipper(final Stream left, final A focus, final Stream right) { + return new Zipper<>(left, focus, right); + } + + /** + * Creates a new Zipper from the given triple. + * + * @param p A triple of the elements before the focus, the focus element, and the elements after the focus, + * respectively. + * @return a new Zipper created from the given triple. + */ + public static Zipper zipper(final P3, A, Stream> p) { + return new Zipper<>(p._1(), p._2(), p._3()); + } + + /** + * First-class constructor of zippers. + * + * @return A function that yields a new zipper given streams on the left and right and a focus element. + */ + public static F3, A, Stream, Zipper> zipper() { + return Zipper::zipper; + } + + /** + * Returns the product-3 representation of this Zipper. + * + * @return the product-3 representation of this Zipper. + */ + public P3, A, Stream> p() { + return P.p(left, focus, right); + } + + /** + * A first-class function that yields the product-3 representation of a given Zipper. + * + * @return A first-class function that yields the product-3 representation of a given Zipper. + */ + public static F, P3, A, Stream>> p_() { + return Zipper::p; + } + + /** + * An Ord instance for Zippers. + * + * @param o An Ord instance for the element type. + * @return An Ord instance for Zippers. + */ + public static Ord> ord(final Ord o) { + final Ord> so = Ord.streamOrd(o); + return Ord.p3Ord(so, o, so).contramap(Zipper.p_()); + } + + @Override + public final boolean equals(Object other) { + return Equal.equals0(Zipper.class, this, other, () -> Equal.zipperEqual(Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.zipperHash(Hash.anyHash()).hash(this); + } + + /** + * An Equal instance for Zippers. + * + * @param e An Equal instance for the element type. + * @return An Equal instance for Zippers. + */ + public static Equal> eq(final Equal e) { + final Equal> se = Equal.streamEqual(e); + return Equal.p3Equal(se, e, se).contramap(Zipper.p_()); + } + + /** + * A Show instance for Zippers. + * + * @param s A Show instance for the element type. + * @return A Show instance for Zippers. + */ + public static Show> show(final Show s) { + final Show> ss = Show.streamShow(s); + return Show.p3Show(ss, s, ss).contramap(Zipper.p_()); + } + + /** + * Maps the given function across the elements of this zipper (covariant functor pattern). + * + * @param f A function to map across this zipper. + * @return A new zipper with the given function applied to all elements. + */ + public Zipper map(final F f) { + return zipper(left.map(f), f.f(focus), right.map(f)); + } + + /** + * Performs a right-fold reduction across this zipper. + * + * @param f The function to apply on each element of this zipper. + * @param z The beginning value to start the application from. + * @return the final result after the right-fold reduction. + */ + public B foldRight(final F> f, final B z) { + return left.foldLeft(flip(f), + right.cons(focus).foldRight(compose( + Function., B, B>andThen().f(P1.__1()), f), z)); + } + + /** + * Creates a new zipper with a single element. + * + * @param a The focus element of the new zipper. + * @return a new zipper with a single element which is in focus. + */ + public static Zipper single(final A a) { + return zipper(Stream.nil(), a, Stream.nil()); + } + + /** + * Possibly create a zipper if the provided stream has at least one element, otherwise None. + * The provided stream's head will be the focus of the zipper, and the rest of the stream will follow + * on the right side. + * + * @param a The stream from which to create a zipper. + * @return a new zipper if the provided stream has at least one element, otherwise None. + */ + @SuppressWarnings("IfMayBeConditional") + public static Option> fromStream(final Stream a) { + if (a.isEmpty()) + return none(); + else + return some(zipper(Stream.nil(), a.head(), a.tail()._1())); + } + + /** + * Possibly create a zipper if the provided stream has at least one element, otherwise None. + * The provided stream's last element will be the focus of the zipper, following the rest of the stream in order, + * to the left. + * + * @param a The stream from which to create a zipper. + * @return a new zipper if the provided stream has at least one element, otherwise None. + */ + public static Option> fromStreamEnd(final Stream a) { + if (a.isEmpty()) + return none(); + else { + final Stream xs = a.reverse(); + return some(zipper(xs.tail()._1(), xs.head(), Stream.nil())); + } + } + + /** + * Returns the focus element of this zipper. + * + * @return the focus element of this zipper. + */ + public A focus() { + return focus; + } + + /** + * Possibly moves the focus to the next element in the list. + * + * @return An optional zipper with the focus moved one element to the right, if there are elements to the right of + * focus, otherwise None. + */ + public Option> next() { + return right.isEmpty() ? Option.none() : some(tryNext()); + } + + /** + * Attempts to move the focus to the next element, or throws an error if there are no more elements. + * + * @return A zipper with the focus moved one element to the right, if there are elements to the right of + * focus, otherwise throws an error. + */ + public Zipper tryNext() { + if (right.isEmpty()) + throw new Error("Tried next at the end of a zipper."); + else + return zipper(left.cons(focus), right.head(), right.tail()._1()); + } + + /** + * Possibly moves the focus to the previous element in the list. + * + * @return An optional zipper with the focus moved one element to the left, if there are elements to the left of + * focus, otherwise None. + */ + public Option> previous() { + return left.isEmpty() ? Option.none() : some(tryPrevious()); + } + + /** + * Attempts to move the focus to the previous element, or throws an error if there are no more elements. + * + * @return A zipper with the focus moved one element to the left, if there are elements to the left of + * focus, otherwise throws an error. + */ + public Zipper tryPrevious() { + if (left.isEmpty()) + throw new Error("Tried previous at the beginning of a zipper."); + else + return zipper(left.tail()._1(), left.head(), right.cons(focus)); + } + + /** + * First-class version of the next() function. + * + * @return A function that moves the given zipper's focus to the next element. + */ + public static F, Option>> next_() { + return Zipper::next; + } + + /** + * First-class version of the previous() function. + * + * @return A function that moves the given zipper's focus to the previous element. + */ + public static F, Option>> previous_() { + return Zipper::previous; + } + + /** + * Inserts an element to the left of the focus, then moves the focus to the new element. + * + * @param a A new element to insert into this zipper. + * @return A new zipper with the given element in focus, and the current focus element on its right. + */ + public Zipper insertLeft(final A a) { + return zipper(left, a, right.cons(focus)); + } + + /** + * Inserts an element to the right of the focus, then moves the focus to the new element. + * + * @param a A new element to insert into this zipper. + * @return A new zipper with the given element in focus, and the current focus element on its left. + */ + public Zipper insertRight(final A a) { + return zipper(left.cons(focus), a, right); + } + + /** + * Possibly deletes the element at the focus, then moves the element on the left into focus. + * If no element is on the left, focus on the element to the right. + * Returns None if the focus element is the only element in this zipper. + * + * @return A new zipper with this zipper's focus element removed, or None if deleting the focus element + * would cause the zipper to be empty. + */ + public Option> deleteLeft() { + return left.isEmpty() && right.isEmpty() + ? Option.none() + : some(zipper(left.isEmpty() ? left : left.tail()._1(), + left.isEmpty() ? right.head() : left.head(), + left.isEmpty() ? right.tail()._1() : right)); + } + + /** + * Possibly deletes the element at the focus, then moves the element on the right into focus. + * If no element is on the right, focus on the element to the left. + * Returns None if the focus element is the only element in this zipper. + * + * @return A new zipper with this zipper's focus element removed, or None if deleting the focus element + * would cause the zipper to be empty. + */ + public Option> deleteRight() { + return left.isEmpty() && right.isEmpty() + ? Option.none() + : some(zipper(right.isEmpty() ? left.tail()._1() : left, + right.isEmpty() ? left.head() : right.head(), + right.isEmpty() ? right : right.tail()._1())); + } + + /** + * Deletes all elements in the zipper except the focus. + * + * @return A new zipper with the focus element as the only element. + */ + public Zipper deleteOthers() { + final Stream nil = nil(); + return zipper(nil, focus, nil); + } + + /** + * Returns the length of this zipper. + * + * @return the length of this zipper. + */ + public int length() { + return foldRight(Function.constant(Integers.add.f(1)), 0); + } + + /** + * Returns whether the focus is on the first element. + * + * @return true if the focus is on the first element, otherwise false. + */ + public boolean atStart() { + return left.isEmpty(); + } + + /** + * Returns whether the focus is on the last element. + * + * @return true if the focus is on the last element, otherwise false. + */ + public boolean atEnd() { + return right.isEmpty(); + } + + /** + * Creates a zipper of variations of this zipper, in which each element is focused, + * with this zipper as the focus of the zipper of zippers (comonad pattern). + * + * @return a zipper of variations of the provided zipper, in which each element is focused, + * with this zipper as the focus of the zipper of zippers. + */ + public Zipper> positions() { + final Stream> left = Stream.unfold( + p -> p.previous().map(join(P.p2())), this); + final Stream> right = Stream.unfold( + p -> p.next().map(join(P.p2())), this); + + return zipper(left, this, right); + } + + /** + * Maps over variations of this zipper, such that the given function is applied to each variation (comonad pattern). + * + * @param f The comonadic function to apply for each variation of this zipper. + * @return A new zipper, with the given function applied for each variation of this zipper. + */ + public Zipper cobind(final F, B> f) { + return positions().map(f); + } + + /** + * Zips the elements of this zipper with a boolean that indicates whether that element has focus. + * All of the booleans will be false, except the focused element. + * + * @return A new zipper of pairs, with each element of this zipper paired with a boolean that is true if that + * element has focus, and false otherwise. + */ + public Zipper> zipWithFocus() { + return zipper(left.zip(repeat(false)), P.p(focus, true), right.zip(repeat(false))); + } + + /** + * Move the focus to the specified index. + * + * @param n The index to which to move the focus. + * @return A new zipper with the focus moved to the specified index, or none if there is no such index. + */ + public Option> move(final int n) { + final int ll = left.length(); + final int rl = right.length(); + Option> p = some(this); + if (n < 0 || n >= length()) + return none(); + else if (ll >= n) + for (int i = ll - n; i > 0; i--) + p = p.bind(Zipper.previous_()); + else if (rl >= n) + for (int i = rl - n; i > 0; i--) + p = p.bind(Zipper.next_()); + return p; + } + + /** + * A first-class version of the move function. + * + * @return A function that moves the focus of the given zipper to the given index. + */ + public static F, Option>>> move() { + return curry((i, a) -> a.move(i)); + } + + /** + * Moves the focus to the element matching the given predicate, if present. + * + * @param p A predicate to match. + * @return A new zipper with the nearest matching element focused if it is present in this zipper. + */ + public Option> find(final F p) { + if (p.f(focus())) + return some(this); + else { + final Zipper> ps = positions(); + return ps.lefts().interleave(ps.rights()).find(zipper -> p.f(zipper.focus())); + } + } + + /** + * Returns the index of the focus. + * + * @return the index of the focus. + */ + public int index() { + return left.length(); + } + + /** + * Move the focus to the next element. If the last element is focused, loop to the first element. + * + * @return A new zipper with the next element focused, unless the last element is currently focused, in which case + * the first element becomes focused. + */ + public Zipper cycleNext() { + if (left.isEmpty() && right.isEmpty()) + return this; + else if (right.isEmpty()) { + final Stream xs = left.reverse(); + return zipper(Stream.nil(), xs.head(), xs.tail()._1().snoc(P.p(focus))); + } else + return tryNext(); + } + + /** + * Move the focus to the previous element. If the first element is focused, loop to the last element. + * + * @return A new zipper with the previous element focused, unless the first element is currently focused, + * in which case the last element becomes focused. + */ + public Zipper cyclePrevious() { + if (left.isEmpty() && right.isEmpty()) + return this; + else if (left.isEmpty()) { + final Stream xs = right.reverse(); + return zipper(xs.tail()._1().snoc(P.p(focus)), xs.head(), Stream.nil()); + } else + return tryPrevious(); + } + + /** + * Possibly deletes the element at the focus, then move the element on the left into focus. If no element is on the + * left, focus on the last element. If the deletion will cause the list to be empty, return None. + * + * @return A new zipper with the focused element removed, and focus on the previous element to the left, or the last + * element if there is no element to the left. + */ + public Option> deleteLeftCycle() { + if (left.isEmpty() && right.isEmpty()) + return none(); + else if (left.isNotEmpty()) + return some(zipper(left.tail()._1(), left.head(), right)); + else { + final Stream xs = right.reverse(); + return some(zipper(xs.tail()._1(), xs.head(), Stream.nil())); + } + } + + /** + * Possibly deletes the element at the focus, then move the element on the right into focus. If no element is on the + * right, focus on the first element. If the deletion will cause the list to be empty, return None. + * + * @return A new zipper with the focused element removed, and focus on the next element to the right, or the first + * element if there is no element to the right. + */ + public Option> deleteRightCycle() { + if (left.isEmpty() && right.isEmpty()) + return none(); + else if (right.isNotEmpty()) + return some(zipper(left, right.head(), right.tail()._1())); + else { + final Stream xs = left.reverse(); + return some(zipper(Stream.nil(), xs.head(), xs.tail()._1())); + } + } + + /** + * Replaces the element in focus with the given element. + * + * @param a An element to replace the focused element with. + * @return A new zipper with the given element in focus. + */ + public Zipper replace(final A a) { + return zipper(left, a, right); + } + + /** + * Returns the Stream representation of this zipper. + * + * @return A stream that contains all the elements of this zipper. + */ + public Stream toStream() { + return left.reverse().snoc(P.p(focus)).append(right); + } + + /** + * Returns a Stream of the elements to the left of focus. + * + * @return a Stream of the elements to the left of focus. + */ + public Stream lefts() { + return left; + } + + /** + * Returns a Stream of the elements to the right of focus. + * + * @return a Stream of the elements to the right of focus. + */ + public Stream rights() { + return right; + } + + /** + * Zips this Zipper with another, applying the given function lock-step over both zippers in both directions. + * The structure of the resulting Zipper is the structural intersection of the two Zippers. + * + * @param bs A Zipper to zip this one with. + * @param f A function with which to zip together the two Zippers. + * @return The result of applying the given function over this Zipper and the given Zipper, location-wise. + */ + public Zipper zipWith(final Zipper bs, final F2 f) { + return f.zipZipperM().f(this, bs); + } + + + /** + * Zips this Zipper with another, applying the given function lock-step over both zippers in both directions. + * The structure of the resulting Zipper is the structural intersection of the two Zippers. + * + * @param bs A Zipper to zip this one with. + * @param f A function with which to zip together the two Zippers. + * @return The result of applying the given function over this Zipper and the given Zipper, location-wise. + */ + public Zipper zipWith(final Zipper bs, final F> f) { + return zipWith(bs, uncurryF2(f)); + } + + /** + * Returns an iterator of all the positions of this Zipper, starting from the leftmost position. + * + * @return An iterator of all the positions of this Zipper, starting from the leftmost position. + */ + public Iterator> iterator() { return positions().toStream().iterator(); } +} diff --git a/core/src/main/java/fj/data/fingertrees/FingerTree.java b/core/src/main/java/fj/data/fingertrees/FingerTree.java index 160b5508..ab0effb0 100644 --- a/core/src/main/java/fj/data/fingertrees/FingerTree.java +++ b/core/src/main/java/fj/data/fingertrees/FingerTree.java @@ -1,268 +1,268 @@ -package fj.data.fingertrees; - -import fj.*; -import fj.data.Option; -import fj.data.Seq; -import fj.data.Stream; - -import static fj.Monoid.intAdditionMonoid; -import static fj.Monoid.intMaxMonoid; -import static fj.data.Stream.nil; - -/** - * Provides 2-3 finger trees, a functional representation of persistent sequences supporting access to the ends in - * amortized O(1) time. Concatenation and splitting time is O(log n) in the size of the smaller piece. - * A general purpose data structure that can serve as a sequence, priority queue, search tree, priority search queue - * and more. - *

      - * This class serves as a datastructure construction kit, rather than a datastructure in its own right. By supplying - * a monoid, a measurement function, insertion, deletion, and so forth, any purely functional datastructure can be - * emulated. See {@link Seq} for an example. - *

      - * Based on "Finger trees: a simple general-purpose data structure", by Ralf Hinze and Ross Paterson. - * - * @param The monoidal type with which to annotate nodes. - * @param The type of the tree's elements. - */ -public abstract class FingerTree { - private final Measured m; - - /** - * Folds the tree to the right with the given function and the given initial element. - * - * @param f A function with which to fold the tree. - * @param z An initial element to apply to the fold. - * @return A reduction of this tree by applying the given function, associating to the right. - */ - public abstract B foldRight(final F> f, final B z); - - public final B foldRight(final F2 f, final B z) { - return foldRight(F2Functions.curry(f), z); - } - - /** - * Folds the tree to the right with the given function. - * - * @param f A function with which to fold the tree. - * @return A reduction of this tree by applying the given function, associating to the right. - */ - public abstract A reduceRight(final F> f); - - /** - * Folds the tree to the left with the given function and the given initial element. - * - * @param f A function with which to fold the tree. - * @param z An initial element to apply to the fold. - * @return A reduction of this tree by applying the given function, associating to the left. - */ - public abstract B foldLeft(final F> f, final B z); - - public final B foldLeft(final F2 f, final B z) { - return foldLeft(F2Functions.curry(f), z); - } - - /** - * Folds the tree to the left with the given function. - * - * @param f A function with which to fold the tree. - * @return A reduction of this tree by applying the given function, associating to the right. - */ - public abstract A reduceLeft(final F> f); - - /** - * Maps the given function across this tree, measuring with the given Measured instance. - * - * @param f A function to map across the values of this tree. - * @param m A measuring with which to annotate the tree. - * @return A new tree with the same structure as this tree, with each element transformed by the given function, - * and nodes annotated according to the given measuring. - */ - public abstract FingerTree map(final F f, final Measured m); - - public final FingerTree filter(final F f) { - FingerTree tree = new Empty<>(m); - return foldLeft((acc, a) -> f.f(a) ? acc.snoc(a) : acc, tree); - } - - /** - * Returns the sum of this tree's annotations. - * - * @return the sum of this tree's annotations. - */ - public abstract V measure(); - - /** - * Indicates whether this tree is empty. - * - * @return true if this tree is the empty tree, otherwise false. - */ - public final boolean isEmpty() { - return this instanceof Empty; - } - - public final Measured measured() { - return m; - } - - /** - * Provides pattern matching on trees. This is the Church encoding of the FingerTree datatype. - * - * @param empty The function to apply to this empty tree. - * @param single A function to apply if this tree contains a single element. - * @param deep A function to apply if this tree contains more than one element. - * @return The result of the function that matches this tree structurally, applied to this tree. - */ - public abstract B match(final F, B> empty, final F, B> single, - final F, B> deep); - - FingerTree(final Measured m) { - this.m = m; - } - - /** - * Constructs a Measured instance for the element type, given a monoid and a measuring function. - * - * @param monoid A monoid for the measures. - * @param measure A function with which to measure element values. - * @return A Measured instance for the given element type, that uses the given monoid and measuring function. - */ - public static Measured measured(final Monoid monoid, final F measure) { - return Measured.measured(monoid, measure); - } - - /** - * Returns a builder of trees and tree components that annotates them using the given Measured instance. - * - * @param m A Measured instance with which to annotate trees, digits, and nodes. - * @return A builder of trees and tree components that annotates them using the given Measured instance. - */ - public static MakeTree mkTree(final Measured m) { - return new MakeTree<>(m); - } - - /** - * Adds the given element to this tree as the first element. - * - * @param a The element to add to the front of this tree. - * @return A new tree with the given element at the front. - */ - public abstract FingerTree cons(final A a); - - /** - * Adds the given element to this tree as the last element. - * - * @param a The element to add to the end of this tree. - * @return A new tree with the given element at the end. - */ - public abstract FingerTree snoc(final A a); - - /** - * The first element of this tree. This is an O(1) operation. - * - * @return The first element if this tree is nonempty, otherwise throws an error. - */ - public abstract A head(); - - public final Option headOption() { - return isEmpty() ? Option.none() : Option.some(head()); - } - - /** - * Performs a reduction on this finger tree using the given arguments. - * - * @param nil The value to return if this finger tree is empty. - * @param cons The function to apply to the head and tail of this finger tree if it is not empty. - * @return A reduction on this finger tree. - */ - public final B uncons(B nil, F2, B> cons) { - return isEmpty() ? nil : cons.f(head(), tail()); - } - - - /** - * The last element of this tree. This is an O(1) operation. - * - * @return The last element if this tree is nonempty, otherwise throws an error. - */ - public abstract A last(); - - /** - * The tree without the first element. This is an O(1) operation. - * - * @return The tree without the first element if this tree is nonempty, otherwise throws an error. - */ - public abstract FingerTree tail(); - - /** - * The tree without the last element. This is an O(1) operation. - * - * @return The tree without the last element if this tree is nonempty, otherwise throws an error. - */ - public abstract FingerTree init(); - - /** - * Appends one finger tree to another. - * - * @param t A finger tree to append to this one. - * @return A new finger tree which is a concatenation of this tree and the given tree. - */ - public abstract FingerTree append(final FingerTree t); - - /** - * Splits this tree into a pair of subtrees at the point where the given predicate, based on the measure, - * changes from false to true. This is a O(log(n)) operation. - * - * @return Pair: the subtree containing elements before the point where pred first holds and the subtree - * containing element at and after the point where pred first holds. Empty if pred never holds. - */ - public final P2, FingerTree> split(final F predicate) { - if (!isEmpty() && predicate.f(measure())) { - final P3, A, FingerTree> lxr = split1(predicate); - return P.p(lxr._1(), lxr._3().cons(lxr._2())); - } else { - return P.p(this, mkTree(m).empty()); - } - } - - /** - * Like split, but returns the element where pred first holds separately. - * - * Throws an error if the tree is empty. - */ - public final P3, A, FingerTree> split1(final F predicate) { - return split1(predicate, measured().zero()); - } - - abstract P3, A, FingerTree> split1(final F predicate, final V acc); - - public abstract P2 lookup(final F o, final int i); - - public abstract int length(); - - public static FingerTree emptyIntAddition() { - return empty(intAdditionMonoid, Function.constant(1)); - } - - /** - * Creates an empty finger tree with elements of type A and node annotations - * of type V. - * - * @param m A monoid to combine node annotations - * @param f Function to convert node element to annotation. - * @return An empty finger tree. - */ - public static FingerTree empty(Monoid m, F f) { - return FingerTree.mkTree(measured(m, f)).empty(); - } - - /** - * Returns a finger tree which combines the integer node annotations with the - * maximum function. A priority queue with integer priorities. - */ - public static FingerTree> emptyIntMax() { - return empty(intMaxMonoid, (P2 p) -> p._1()); - } - - public abstract Stream toStream(); - -} +package fj.data.fingertrees; + +import fj.*; +import fj.data.Option; +import fj.data.Seq; +import fj.data.Stream; + +import static fj.Monoid.intAdditionMonoid; +import static fj.Monoid.intMaxMonoid; +import static fj.data.Stream.nil; + +/** + * Provides 2-3 finger trees, a functional representation of persistent sequences supporting access to the ends in + * amortized O(1) time. Concatenation and splitting time is O(log n) in the size of the smaller piece. + * A general purpose data structure that can serve as a sequence, priority queue, search tree, priority search queue + * and more. + *

      + * This class serves as a datastructure construction kit, rather than a datastructure in its own right. By supplying + * a monoid, a measurement function, insertion, deletion, and so forth, any purely functional datastructure can be + * emulated. See {@link Seq} for an example. + *

      + * Based on "Finger trees: a simple general-purpose data structure", by Ralf Hinze and Ross Paterson. + * + * @param The monoidal type with which to annotate nodes. + * @param The type of the tree's elements. + */ +public abstract class FingerTree { + private final Measured m; + + /** + * Folds the tree to the right with the given function and the given initial element. + * + * @param f A function with which to fold the tree. + * @param z An initial element to apply to the fold. + * @return A reduction of this tree by applying the given function, associating to the right. + */ + public abstract B foldRight(final F> f, final B z); + + public final B foldRight(final F2 f, final B z) { + return foldRight(f.curry(), z); + } + + /** + * Folds the tree to the right with the given function. + * + * @param f A function with which to fold the tree. + * @return A reduction of this tree by applying the given function, associating to the right. + */ + public abstract A reduceRight(final F> f); + + /** + * Folds the tree to the left with the given function and the given initial element. + * + * @param f A function with which to fold the tree. + * @param z An initial element to apply to the fold. + * @return A reduction of this tree by applying the given function, associating to the left. + */ + public abstract B foldLeft(final F> f, final B z); + + public final B foldLeft(final F2 f, final B z) { + return foldLeft(f.curry(), z); + } + + /** + * Folds the tree to the left with the given function. + * + * @param f A function with which to fold the tree. + * @return A reduction of this tree by applying the given function, associating to the right. + */ + public abstract A reduceLeft(final F> f); + + /** + * Maps the given function across this tree, measuring with the given Measured instance. + * + * @param f A function to map across the values of this tree. + * @param m A measuring with which to annotate the tree. + * @return A new tree with the same structure as this tree, with each element transformed by the given function, + * and nodes annotated according to the given measuring. + */ + public abstract FingerTree map(final F f, final Measured m); + + public final FingerTree filter(final F f) { + FingerTree tree = new Empty<>(m); + return foldLeft((acc, a) -> f.f(a) ? acc.snoc(a) : acc, tree); + } + + /** + * Returns the sum of this tree's annotations. + * + * @return the sum of this tree's annotations. + */ + public abstract V measure(); + + /** + * Indicates whether this tree is empty. + * + * @return true if this tree is the empty tree, otherwise false. + */ + public final boolean isEmpty() { + return this instanceof Empty; + } + + public final Measured measured() { + return m; + } + + /** + * Provides pattern matching on trees. This is the Church encoding of the FingerTree datatype. + * + * @param empty The function to apply to this empty tree. + * @param single A function to apply if this tree contains a single element. + * @param deep A function to apply if this tree contains more than one element. + * @return The result of the function that matches this tree structurally, applied to this tree. + */ + public abstract B match(final F, B> empty, final F, B> single, + final F, B> deep); + + FingerTree(final Measured m) { + this.m = m; + } + + /** + * Constructs a Measured instance for the element type, given a monoid and a measuring function. + * + * @param monoid A monoid for the measures. + * @param measure A function with which to measure element values. + * @return A Measured instance for the given element type, that uses the given monoid and measuring function. + */ + public static Measured measured(final Monoid monoid, final F measure) { + return Measured.measured(monoid, measure); + } + + /** + * Returns a builder of trees and tree components that annotates them using the given Measured instance. + * + * @param m A Measured instance with which to annotate trees, digits, and nodes. + * @return A builder of trees and tree components that annotates them using the given Measured instance. + */ + public static MakeTree mkTree(final Measured m) { + return new MakeTree<>(m); + } + + /** + * Adds the given element to this tree as the first element. + * + * @param a The element to add to the front of this tree. + * @return A new tree with the given element at the front. + */ + public abstract FingerTree cons(final A a); + + /** + * Adds the given element to this tree as the last element. + * + * @param a The element to add to the end of this tree. + * @return A new tree with the given element at the end. + */ + public abstract FingerTree snoc(final A a); + + /** + * The first element of this tree. This is an O(1) operation. + * + * @return The first element if this tree is nonempty, otherwise throws an error. + */ + public abstract A head(); + + public final Option headOption() { + return isEmpty() ? Option.none() : Option.some(head()); + } + + /** + * Performs a reduction on this finger tree using the given arguments. + * + * @param nil The value to return if this finger tree is empty. + * @param cons The function to apply to the head and tail of this finger tree if it is not empty. + * @return A reduction on this finger tree. + */ + public final B uncons(B nil, F2, B> cons) { + return isEmpty() ? nil : cons.f(head(), tail()); + } + + + /** + * The last element of this tree. This is an O(1) operation. + * + * @return The last element if this tree is nonempty, otherwise throws an error. + */ + public abstract A last(); + + /** + * The tree without the first element. This is an O(1) operation. + * + * @return The tree without the first element if this tree is nonempty, otherwise throws an error. + */ + public abstract FingerTree tail(); + + /** + * The tree without the last element. This is an O(1) operation. + * + * @return The tree without the last element if this tree is nonempty, otherwise throws an error. + */ + public abstract FingerTree init(); + + /** + * Appends one finger tree to another. + * + * @param t A finger tree to append to this one. + * @return A new finger tree which is a concatenation of this tree and the given tree. + */ + public abstract FingerTree append(final FingerTree t); + + /** + * Splits this tree into a pair of subtrees at the point where the given predicate, based on the measure, + * changes from false to true. This is a O(log(n)) operation. + * + * @return Pair: the subtree containing elements before the point where pred first holds and the subtree + * containing element at and after the point where pred first holds. Empty if pred never holds. + */ + public final P2, FingerTree> split(final F predicate) { + if (!isEmpty() && predicate.f(measure())) { + final P3, A, FingerTree> lxr = split1(predicate); + return P.p(lxr._1(), lxr._3().cons(lxr._2())); + } else { + return P.p(this, mkTree(m).empty()); + } + } + + /** + * Like split, but returns the element where pred first holds separately. + * + * Throws an error if the tree is empty. + */ + public final P3, A, FingerTree> split1(final F predicate) { + return split1(predicate, measured().zero()); + } + + abstract P3, A, FingerTree> split1(final F predicate, final V acc); + + public abstract P2 lookup(final F o, final int i); + + public abstract int length(); + + public static FingerTree emptyIntAddition() { + return empty(intAdditionMonoid, Function.constant(1)); + } + + /** + * Creates an empty finger tree with elements of type A and node annotations + * of type V. + * + * @param m A monoid to combine node annotations + * @param f Function to convert node element to annotation. + * @return An empty finger tree. + */ + public static FingerTree empty(Monoid m, F f) { + return FingerTree.mkTree(measured(m, f)).empty(); + } + + /** + * Returns a finger tree which combines the integer node annotations with the + * maximum function. A priority queue with integer priorities. + */ + public static FingerTree> emptyIntMax() { + return empty(intMaxMonoid, (P2 p) -> p._1()); + } + + public abstract Stream toStream(); + +} diff --git a/core/src/test/java/fj/FFunctionsTest.java b/core/src/test/java/fj/FFunctionsTest.java index e13b1f2b..e0df9164 100644 --- a/core/src/test/java/fj/FFunctionsTest.java +++ b/core/src/test/java/fj/FFunctionsTest.java @@ -1,43 +1,47 @@ -package fj; - -import fj.data.Tree; -import fj.data.TreeZipper; -import org.junit.Test; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; - -public class FFunctionsTest { - @Test - public void testApply() { - F8 f8 = - (i1, i2, i3, i4, i5, i6, i7, i8) -> - i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8; - F7 f7 = F8Functions.f(f8, 8); - F6 f6 = - F7Functions.f(f7, 7); - F5 f5 = F6Functions.f(f6, 6); - F4 f4 = F5Functions.f(f5, 5); - F3 f3 = F4Functions.f(f4, 4); - F2 f2 = F3Functions.f(f3, 3); - F f1 = F2Functions.f(f2, 2); - assertThat(F1Functions.f(f1, 1).f(), is(36)); - } - - @Test - public void testTreeK() { - final Tree t1 = F1Functions.treeK(Function.identity()).f(1); - final Tree t2 = F1Functions.treeK(Function.identity()).f(2); - assertThat(F1Functions.mapTree(i -> i + 1).f(t1), - is(F1Functions.mapTree(i -> i * 1).f(t2))); - } - - @Test - public void testTreeZipperK() { - final TreeZipper tz1 = F1Functions.treeZipperK(Function.identity()).f(1); - final TreeZipper tz2 = F1Functions.treeZipperK(Function.identity()).f(2); - assertThat(F1Functions.mapTreeZipper(i -> i + 1).f(tz1), - is(F1Functions.mapTreeZipper(i -> i * 1).f(tz2))); - } -} +package fj; + +import fj.data.Tree; +import fj.data.TreeZipper; +import org.junit.Test; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.*; + +public class FFunctionsTest { + @Test + public void testApply() { + F8 f8 = + (i1, i2, i3, i4, i5, i6, i7, i8) -> + i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8; + F7 f7 = F8Functions.f(f8, 8); + F6 f6 = + F7Functions.f(f7, 7); + F5 f5 = F6Functions.f(f6, 6); + F4 f4 = F5Functions.f(f5, 5); + F3 f3 = F4Functions.f(f4, 4); + F2 f2 = F3Functions.f(f3, 3); + F f1 = f2.f(2); + assertThat(f1.f(1), is(36)); + } + + @Test + public void testTreeK() { + final Tree t1 = Function.identity().treeK().f(1); + final Tree t2 = Function.identity().treeK().f(2); + F f = i -> i + 1; + F g = i -> i * 1; + assertThat(f.mapTree().f(t1), + is(g.mapTree().f(t2))); + } + + @Test + public void testTreeZipperK() { + final TreeZipper tz1 = Function.identity().treeZipperK().f(1); + final TreeZipper tz2 = Function.identity().treeZipperK().f(2); + F f = i -> i + 1; + F g = i -> i * 1; + assertThat(f.mapTreeZipper().f(tz1), + is(g.mapTreeZipper().f(tz2))); + } +} diff --git a/core/src/test/java/fj/FWFunctionsTest.java b/core/src/test/java/fj/FWFunctionsTest.java deleted file mode 100644 index 1c034ab9..00000000 --- a/core/src/test/java/fj/FWFunctionsTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package fj; - -import org.junit.Test; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -public class FWFunctionsTest { - @Test - public void testLift1() { - F f = i -> i + 1; - F1W f1w = F1W.lift(f); - assertThat(f1w.f(1), is(2)); - } - - @Test - public void testLift2() { - F2 f2 = (i, j) -> i + j; - F2W f2w = F2W.lift(f2); - assertThat(f2w.f(1, 2), is(3)); - } - -} diff --git a/core/src/test/java/fj/data/IOFunctionsTest.java b/core/src/test/java/fj/data/IOFunctionsTest.java index 5213878c..adad0d11 100644 --- a/core/src/test/java/fj/data/IOFunctionsTest.java +++ b/core/src/test/java/fj/data/IOFunctionsTest.java @@ -107,7 +107,8 @@ public void testReplicateM() throws IOException { @Test public void testLift() throws IOException { final IO readName = () -> new BufferedReader(new StringReader("foo")).readLine(); - final F> upperCaseAndPrint = F1Functions., String>o(this::println).f(String::toUpperCase); + F> f = this::println; + final F> upperCaseAndPrint = f.o().f(String::toUpperCase); final IO readAndPrintUpperCasedName = IOFunctions.bind(readName, upperCaseAndPrint); assertThat(readAndPrintUpperCasedName.run(), is("FOO")); } diff --git a/core/src/test/java/fj/function/StringsTest.java b/core/src/test/java/fj/function/StringsTest.java index 92cf44d3..c13e1b81 100644 --- a/core/src/test/java/fj/function/StringsTest.java +++ b/core/src/test/java/fj/function/StringsTest.java @@ -3,7 +3,6 @@ import fj.Function; import org.junit.Test; -import static fj.F1Functions.o; import static fj.Function.compose; import static fj.function.Strings.*; import static org.junit.Assert.*; @@ -17,7 +16,7 @@ public void testLines() { @Test public void testLinesEmpty() { - assertThat(o(unlines(), lines()).f(""), is("")); + assertThat(unlines().o(lines()).f(""), is("")); } @Test diff --git a/demo/src/main/java/fj/demo/Comonad_example.java b/demo/src/main/java/fj/demo/Comonad_example.java index 8d5bc683..3b5661f8 100644 --- a/demo/src/main/java/fj/demo/Comonad_example.java +++ b/demo/src/main/java/fj/demo/Comonad_example.java @@ -1,6 +1,5 @@ package fj.demo; -import fj.F1Functions; import fj.P; import static fj.data.List.asString; import static fj.data.List.fromString; @@ -26,7 +25,7 @@ public static Stream> perms(final Stream s) { for (final Zipper z : fromStream(s)) r = join(z.cobind(zp -> perms(zp.lefts().reverse().append(zp.rights())).map( - F1Functions.o(Stream.cons().f(zp.focus()), P.p1()) + Stream.cons().f(zp.focus()).o(P.p1()) ) ).toStream()); return r; diff --git a/demo/src/main/java/fj/demo/IODemo.java b/demo/src/main/java/fj/demo/IODemo.java index 6451a46b..ea313e77 100644 --- a/demo/src/main/java/fj/demo/IODemo.java +++ b/demo/src/main/java/fj/demo/IODemo.java @@ -4,7 +4,6 @@ import fj.data.IOFunctions; import fj.data.LazyString; -import static fj.F1W.lift; import static fj.data.IOFunctions.interact; import static fj.data.IOFunctions.runSafe; import static fj.data.LazyString.lines_; @@ -29,7 +28,7 @@ public static void main(String[] args) { * and prints that last line. */ public final void readFirstShortLine() { - F f = lift(lines_()).andThen(l -> l.filter(s -> s.length() < 3)).andThen(unlines_()); + F f = lines_().andThen(l -> l.filter(s -> s.length() < 3)).andThen(unlines_()); runSafe(interact(f)); } @@ -37,7 +36,7 @@ public final void readFirstShortLine() { * Read a stream of input lazily using interact, in effect reading the first line */ public final void readFirstLine() { - F f = lift(LazyString::lines).andThen(unlines_()); + F f = lines_().andThen(unlines_()); runSafe(interact(f)); } diff --git a/demo/src/main/java/fj/demo/IOWalkthrough.java b/demo/src/main/java/fj/demo/IOWalkthrough.java index 609d2502..5c1c4663 100644 --- a/demo/src/main/java/fj/demo/IOWalkthrough.java +++ b/demo/src/main/java/fj/demo/IOWalkthrough.java @@ -1,8 +1,6 @@ package fj.demo; import fj.F; -import fj.F1Functions; -import fj.F1W; import fj.Unit; import fj.data.IO; import fj.data.IOFunctions; @@ -49,7 +47,7 @@ public static void main(String[] args) { // now we create a function which takes a string, upper cases it and creates an IO value that would print the upper cased string if executed - final F> upperCaseAndPrint = F1Functions., String>o(IOFunctions::stdoutPrintln).f(String::toUpperCase); + final F> upperCaseAndPrint = s -> stdoutPrintln(s.toUpperCase()); // we now want to compose reading the name with printing it, for that we need to have access to the runtime value that is returned when the // IO value for read is executed, hence we use fj.data.IOFunctions.bind instead of fj.data.IOFunctions.append @@ -73,10 +71,11 @@ public static void main(String[] args) { // assigning the functions to variables like above, but you can use the fj.F1W syntax wrapper for composing single-argument functions and fj.data.IOW // for composing IO values instead, the entire program can be written like so: + F f = String::toUpperCase; IOW.lift(stdoutPrintln("What's your name again?")) .append(stdoutPrint("Name: ")) .append(stdinReadLine()) - .bind(F1W.lift((String s) -> s.toUpperCase()).andThen(IOFunctions::stdoutPrintln)) + .bind(f.andThen(IOFunctions::stdoutPrintln)) .safe().run().on((IOException e) -> { e.printStackTrace(); return Unit.unit(); }); } } diff --git a/demo/src/main/java/fj/demo/Primes2.java b/demo/src/main/java/fj/demo/Primes2.java index 96a852f3..37bfb249 100644 --- a/demo/src/main/java/fj/demo/Primes2.java +++ b/demo/src/main/java/fj/demo/Primes2.java @@ -1,7 +1,5 @@ package fj.demo; -import fj.F1Functions; - import static fj.data.Enumerator.naturalEnumerator; import fj.Show; @@ -21,7 +19,7 @@ public class Primes2 { // Finds primes in a given stream. public static Stream sieve(final Stream xs) { - return cons(xs.head(), () -> sieve(xs.tail()._1().removeAll(F1Functions.o(naturalOrd.equal().eq(ZERO), mod.f(xs.head()))))); + return cons(xs.head(), () -> sieve(xs.tail()._1().removeAll(naturalOrd.equal().eq(ZERO).o(mod.f(xs.head()))))); } // A stream of all primes less than n. diff --git a/demo/src/main/java/fj/demo/WriterDemo_Halver.java b/demo/src/main/java/fj/demo/WriterDemo_Halver.java index a2a270f6..8473e588 100644 --- a/demo/src/main/java/fj/demo/WriterDemo_Halver.java +++ b/demo/src/main/java/fj/demo/WriterDemo_Halver.java @@ -4,7 +4,6 @@ import fj.P2; import fj.data.Writer; -import static fj.F1Functions.map; import static fj.Monoid.stringMonoid; /** @@ -24,7 +23,7 @@ static void testWriter() { Integer init = 32; P2 p1 = half().f(init).flatMap(half()).flatMap(half()).run(); System.out.println(p1); - System.out.println(map(half(), w -> w.flatMap(half()).flatMap(half()).run()).f(init)); + System.out.println(half().map(w -> w.flatMap(half()).flatMap(half()).run()).f(init)); } } diff --git a/demo/src/main/java/fj/demo/concurrent/MapReduce.java b/demo/src/main/java/fj/demo/concurrent/MapReduce.java index 3cb2d761..38a62caa 100644 --- a/demo/src/main/java/fj/demo/concurrent/MapReduce.java +++ b/demo/src/main/java/fj/demo/concurrent/MapReduce.java @@ -35,14 +35,15 @@ public static Promise countWords(final List> documents, // Main program does the requisite IO gymnastics public static void main(final String[] args) { + F z = fileName -> { + try { + return new BufferedReader(new FileReader(new File(fileName))); + } catch (FileNotFoundException e) { + throw new Error(e); + } + }; final List> documents = list(args).map( - F1Functions.andThen(fileName -> { - try { - return new BufferedReader(new FileReader(new File(fileName))); - } catch (FileNotFoundException e) { - throw new Error(e); - } - }, new F>() { + z.andThen(new F>() { public Stream f(final BufferedReader reader) { final Option s; try { diff --git a/demo/src/main/java/fj/demo/euler/Problem2.java b/demo/src/main/java/fj/demo/euler/Problem2.java index eda4a72d..a5639ab9 100644 --- a/demo/src/main/java/fj/demo/euler/Problem2.java +++ b/demo/src/main/java/fj/demo/euler/Problem2.java @@ -1,8 +1,6 @@ package fj.demo.euler; -import fj.F1Functions; import fj.F2; -import fj.F2Functions; import fj.data.Stream; import static fj.data.Stream.cons; import static fj.function.Integers.even; @@ -23,13 +21,13 @@ public static void main(final String[] args) { static void java7() { final Stream fibs = new F2>() { public Stream f(final Integer a, final Integer b) { - return cons(a, F1Functions.lazy(F2Functions.curry(this).f(b)).f(a + b)); + return cons(a, this.curry().f(b).lazy().f(a + b)); } }.f(1, 2); out.println(sum(fibs.filter(even).takeWhile(intOrd.isLessThan(4000001)).toList())); } - static F2> fibsJava8 = (a, b) -> cons(a, F1Functions.lazy(F2Functions.curry(Problem2.fibsJava8).f(b)).f(a + b)); + static F2> fibsJava8 = (a, b) -> cons(a, Problem2.fibsJava8.curry().f(b).lazy().f(a + b)); static void java8() { out.println(sum(fibsJava8.f(1, 2).filter(even).takeWhile(intOrd.isLessThan(4000001)).toList())); diff --git a/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala index dd95dabf..dfde81e3 100755 --- a/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala +++ b/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala @@ -1,119 +1,119 @@ -package fj -package data - -import fj.function.Effect1 -import org.scalacheck.Prop._ -import ArbitraryHashMap._ -import Equal._ -import Hash._ -import Ord._ -import fj.data.Option._ -import scala.collection.JavaConversions._ -import org.scalacheck.{Arbitrary, Properties} -import data.ArbitraryList._ -import org.scalacheck.Arbitrary._ -import java.util.Map - -object CheckHashMap extends Properties("HashMap") { - implicit val equalInt: Equal[Int] = intEqual contramap ((x: Int) => (x: java.lang.Integer)) - implicit val hashInt: Hash[Int] = intHash contramap ((x: Int) => (x: java.lang.Integer)) - - implicit def arbitraryListOfIterableP2: Arbitrary[java.lang.Iterable[P2[Int, String]]] = - Arbitrary(listOf(arbitrary[(Int, String)]) - .map(_.map((tuple: (Int, String)) => P.p(tuple._1, tuple._2)) - .asInstanceOf[java.lang.Iterable[P2[Int, String]]])) - - property("eq") = forAll((m: HashMap[Int, String], x: Int, y: Int) => m.eq(x, y) == equalInt.eq(x, y)) - - property("hash") = forAll((m: HashMap[Int, String], x: Int) => m.hash(x) == hashInt.hash(x)) - - property("get") = forAll((m: HashMap[Int, String], k: Int) => optionEqual(stringEqual).eq(m.get(k), m.get.f(k))) - - property("set") = forAll((m: HashMap[Int, String], k: Int, v: String) => { - m.set(k, v) - m.get(k).some == v - }) - - property("clear") = forAll((m: HashMap[Int, String], k: Int) => { - m.clear - m.get(k).isNone - }) - - property("contains") = forAll((m: HashMap[Int, String], k: Int) => m.get(k).isSome == m.contains(k)) - - property("keys") = forAll((m: HashMap[Int, String]) => m.keys.forall((k: Int) => (m.get(k).isSome): java.lang.Boolean)) - - property("isEmpty") = forAll((m: HashMap[Int, String], k: Int) => m.get(k).isNone || !m.isEmpty) - - property("size") = forAll((m: HashMap[Int, String], k: Int) => m.get(k).isNone || m.size != 0) - - property("delete") = forAll((m: HashMap[Int, String], k: Int) => { - m.delete(k) - m.get(k).isNone - }) - - property("toList") = forAll((m: HashMap[Int, String]) => { - val list = m.toList - list.length() == m.keys().length() && list.forall((entry: P2[Int, String]) => - optionEqual(stringEqual).eq(m.get(entry._1()), some(entry._2())).asInstanceOf[java.lang.Boolean]) - }) - - property("getDelete") = forAll((m: HashMap[Int, String], k: Int) => { - val x = m.get(k) - val y = m.getDelete(k) - val z = m.get(k) - - z.isNone && optionEqual(stringEqual).eq(x, y) - }) - - property("from") = forAll((entries: java.lang.Iterable[P2[Int, String]]) => { - val map = HashMap.iterableHashMap[Int, String](equalInt, hashInt, entries) - entries.groupBy(_._1) - .forall((e: (Int, Iterable[P2[Int, String]])) => e._2 - .exists((elem: P2[Int, String]) => optionEqual(stringEqual).eq(map.get(e._1), Option.some(elem._2)))) - }) - - property("map") = forAll((m: HashMap[Int, String]) => { - val keyFunction: F[Int, String] = (i: Int) => i.toString - val valueFunction: (String) => String = (s: String) => s + "a" - val mapped = m.map(keyFunction, valueFunction, stringEqual, stringHash) - val keysAreEqual = m.keys().map(keyFunction).toSet == mapped.keys.toSet - val appliedFunctionsToKeysAndValues: Boolean = m.keys().forall((key: Int) => { - val mappedValue = mapped.get(keyFunction.f(key)) - val oldValueMapped = some(valueFunction.f(m.get(key).some())) - Equal.optionEqual(stringEqual).eq(mappedValue, oldValueMapped) - }) - - keysAreEqual && appliedFunctionsToKeysAndValues - }) - - property("toMap") = forAll((m: HashMap[Int, String]) => { - val toMap: Map[Int, String] = m.toMap - m.keys().forall((key: Int) => m.get(key).some() == toMap.get(key)) - }) - - property("fromMap") = forAll((m: HashMap[Int, String]) => { - val map = new java.util.HashMap[Int, String]() - m.keys().foreach((key: Int) => { - map.put(key, m.get(key).some()) - Unit.unit() - }) - val fromMap: HashMap[Int, String] = new HashMap[Int, String](map) - val keysAreEqual = m.keys.toSet == fromMap.keys.toSet - val valuesAreEqual = m.keys().forall((key: Int) => - optionEqual(stringEqual).eq(m.get(key), fromMap.get(key))) - keysAreEqual && valuesAreEqual - }) - - property("No null values") = forAll((m: List[Int]) => { - val map = HashMap.hashMap[Int, Int]() - m.foreachDoEffect(new Effect1[Int] { - def f(a: Int) { - map.set(a, null.asInstanceOf[Int]) - } - }) - m.forall(new F[Int, java.lang.Boolean]() { - def f(a: Int) = map.contains(a) == false - }) - }) +package fj +package data + +import fj.function.Effect1 +import org.scalacheck.Prop._ +import ArbitraryHashMap._ +import Equal._ +import Hash._ +import Ord._ +import fj.data.Option._ +import scala.collection.JavaConversions._ +import org.scalacheck.{Arbitrary, Properties} +import data.ArbitraryList._ +import org.scalacheck.Arbitrary._ +import java.util.Map + +object CheckHashMap extends Properties("HashMap") { + implicit val equalInt: Equal[Int] = intEqual contramap ((x: Int) => (x: java.lang.Integer)) + implicit val hashInt: Hash[Int] = intHash contramap ((x: Int) => (x: java.lang.Integer)) + + implicit def arbitraryListOfIterableP2: Arbitrary[java.lang.Iterable[P2[Int, String]]] = + Arbitrary(listOf(arbitrary[(Int, String)]) + .map(_.map((tuple: (Int, String)) => P.p(tuple._1, tuple._2)) + .asInstanceOf[java.lang.Iterable[P2[Int, String]]])) + + property("eq") = forAll((m: HashMap[Int, String], x: Int, y: Int) => m.eq(x, y) == equalInt.eq(x, y)) + + property("hash") = forAll((m: HashMap[Int, String], x: Int) => m.hash(x) == hashInt.hash(x)) + + property("get") = forAll((m: HashMap[Int, String], k: Int) => optionEqual(stringEqual).eq(m.get(k), m.get.f(k))) + + property("set") = forAll((m: HashMap[Int, String], k: Int, v: String) => { + m.set(k, v) + m.get(k).some == v + }) + + property("clear") = forAll((m: HashMap[Int, String], k: Int) => { + m.clear + m.get(k).isNone + }) + + property("contains") = forAll((m: HashMap[Int, String], k: Int) => m.get(k).isSome == m.contains(k)) + + property("keys") = forAll((m: HashMap[Int, String]) => m.keys.forall((k: Int) => (m.get(k).isSome): java.lang.Boolean)) + + property("isEmpty") = forAll((m: HashMap[Int, String], k: Int) => m.get(k).isNone || !m.isEmpty) + + property("size") = forAll((m: HashMap[Int, String], k: Int) => m.get(k).isNone || m.size != 0) + + property("delete") = forAll((m: HashMap[Int, String], k: Int) => { + m.delete(k) + m.get(k).isNone + }) + + property("toList") = forAll((m: HashMap[Int, String]) => { + val list = m.toList + list.length() == m.keys().length() && list.forall((entry: P2[Int, String]) => + optionEqual(stringEqual).eq(m.get(entry._1()), some(entry._2())).asInstanceOf[java.lang.Boolean]) + }) + + property("getDelete") = forAll((m: HashMap[Int, String], k: Int) => { + val x = m.get(k) + val y = m.getDelete(k) + val z = m.get(k) + + z.isNone && optionEqual(stringEqual).eq(x, y) + }) + + property("from") = forAll((entries: java.lang.Iterable[P2[Int, String]]) => { + val map = HashMap.iterableHashMap[Int, String](equalInt, hashInt, entries) + entries.groupBy(_._1) + .forall((e: (Int, Iterable[P2[Int, String]])) => e._2 + .exists((elem: P2[Int, String]) => optionEqual(stringEqual).eq(map.get(e._1), Option.some(elem._2)))) + }) + + property("map") = forAll((m: HashMap[Int, String]) => { + val keyFunction: F[Int, String] = (i: Int) => i.toString + val valueFunction: (String) => String = (s: String) => s + "a" + val mapped = m.map(keyFunction, valueFunction, stringEqual, stringHash) + val keysAreEqual = m.keys().map(keyFunction).toSet == mapped.keys.toSet + val appliedFunctionsToKeysAndValues: Boolean = m.keys().forall((key: Int) => { + val mappedValue = mapped.get(keyFunction.f(key)) + val oldValueMapped = some(valueFunction.f(m.get(key).some())) + Equal.optionEqual(stringEqual).eq(mappedValue, oldValueMapped) + }) + + keysAreEqual && appliedFunctionsToKeysAndValues + }) + + property("toMap") = forAll((m: HashMap[Int, String]) => { + val toMap: Map[Int, String] = m.toMap + m.keys().forall((key: Int) => m.get(key).some() == toMap.get(key)) + }) + + property("fromMap") = forAll((m: HashMap[Int, String]) => { + val map = new java.util.HashMap[Int, String]() + m.keys().foreach((key: Int) => { + map.put(key, m.get(key).some()) + Unit.unit() + }) + val fromMap: HashMap[Int, String] = new HashMap[Int, String](map) + val keysAreEqual = m.keys.toSet == fromMap.keys.toSet + val valuesAreEqual = m.keys().forall((key: Int) => + optionEqual(stringEqual).eq(m.get(key), fromMap.get(key))) + keysAreEqual && valuesAreEqual + }) + + property("No null values") = forAll((list: List[Int]) => { + val m = HashMap.hashMap[Int, Int]() + list.foreachDoEffect(new Effect1[Int] { + def f(a: Int) { + m.set(a, null.asInstanceOf[Int]) + } + }) + list.forall(new F[Int, java.lang.Boolean]() { + def f(a: Int) = m.contains(a) == false + }) + }) } \ No newline at end of file diff --git a/props-core-scalacheck/src/test/scala/fj/data/CheckIteratee.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckIteratee.scala index 86feeb7a..ff085f87 100644 --- a/props-core-scalacheck/src/test/scala/fj/data/CheckIteratee.scala +++ b/props-core-scalacheck/src/test/scala/fj/data/CheckIteratee.scala @@ -7,7 +7,6 @@ import Unit.unit import List.{list, nil} import Option.{some, none} import fj.Function._ -import fj.F1Functions import fj.data.Iteratee._ import org.scalacheck.Prop._ @@ -50,7 +49,7 @@ object CheckIteratee extends Properties("Iteratee") { var tail = l while(!isDone(i) && !tail.isEmpty) { val input = Input.el(tail.head) - val cont: F[F[Input[E], IterV[E, A]], P1[IterV[E, A]]] = F1Functions.`lazy`(Function.apply(input)) + val cont: F[F[Input[E], IterV[E, A]], P1[IterV[E, A]]] = Function.apply(input).`lazy`() i = i.fold(done, cont)._1 tail = tail.tail } diff --git a/props-core/src/test/java/fj/data/ReaderTest.java b/props-core/src/test/java/fj/data/ReaderTest.java index 87e2b52a..044595d0 100644 --- a/props-core/src/test/java/fj/data/ReaderTest.java +++ b/props-core/src/test/java/fj/data/ReaderTest.java @@ -5,8 +5,6 @@ import fj.test.*; import org.junit.Test; -import static fj.F1Functions.bind; -import static fj.F1Functions.map; import static fj.test.Arbitrary.*; import static fj.test.Cogen.cogenInteger; import static fj.test.Property.prop; @@ -44,7 +42,7 @@ public void testMapProp() { arbF(cogenInteger, arbInteger), arbInteger, (f, g, i) -> { - int expected = map(f, g).f(i); + int expected = f.map(g).f(i); // System.out.println(String.format("input: %d, result: %d", i, expected)); return prop(expected == Reader.unit(f).map(g).f(i)); }); @@ -59,7 +57,7 @@ public void testFlatMapProp() { a, arbInteger, (f, g, i) -> { - int expected = bind(f, j -> g.f(j).getFunction()).f(i); + int expected = f.bind(j -> g.f(j).getFunction()).f(i); // System.out.println(String.format("input: %d, result: %d", i, expected)); return prop(expected == Reader.unit(f).flatMap(g).f(i)); } From 2f005d9ff90641085e7678f9199a171177813af8 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 2 Apr 2021 00:27:14 +1000 Subject: [PATCH 127/173] Removed FxFunctions and FxW for arity 3-8 --- core/src/main/java/fj/F3Functions.java | 22 ---------------------- core/src/main/java/fj/F3W.java | 8 -------- core/src/main/java/fj/F4Functions.java | 21 --------------------- core/src/main/java/fj/F4W.java | 9 --------- core/src/main/java/fj/F5Functions.java | 21 --------------------- core/src/main/java/fj/F5W.java | 9 --------- core/src/main/java/fj/F6Functions.java | 22 ---------------------- core/src/main/java/fj/F6W.java | 9 --------- core/src/main/java/fj/F7Functions.java | 22 ---------------------- core/src/main/java/fj/F7W.java | 9 --------- core/src/main/java/fj/F8Functions.java | 20 -------------------- core/src/main/java/fj/F8W.java | 9 --------- core/src/test/java/fj/FFunctionsTest.java | 17 ----------------- 13 files changed, 198 deletions(-) delete mode 100644 core/src/main/java/fj/F3Functions.java delete mode 100644 core/src/main/java/fj/F3W.java delete mode 100644 core/src/main/java/fj/F4Functions.java delete mode 100644 core/src/main/java/fj/F4W.java delete mode 100644 core/src/main/java/fj/F5Functions.java delete mode 100644 core/src/main/java/fj/F5W.java delete mode 100644 core/src/main/java/fj/F6Functions.java delete mode 100644 core/src/main/java/fj/F6W.java delete mode 100644 core/src/main/java/fj/F7Functions.java delete mode 100644 core/src/main/java/fj/F7W.java delete mode 100644 core/src/main/java/fj/F8Functions.java delete mode 100644 core/src/main/java/fj/F8W.java diff --git a/core/src/main/java/fj/F3Functions.java b/core/src/main/java/fj/F3Functions.java deleted file mode 100644 index 18e811d9..00000000 --- a/core/src/main/java/fj/F3Functions.java +++ /dev/null @@ -1,22 +0,0 @@ -package fj; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public final class F3Functions { - - - private F3Functions() { - } - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - public static F2 f(final F3 f, final A a) { - return (b, c) -> f.f(a, b, c); - } - -} diff --git a/core/src/main/java/fj/F3W.java b/core/src/main/java/fj/F3W.java deleted file mode 100644 index d6ffa80b..00000000 --- a/core/src/main/java/fj/F3W.java +++ /dev/null @@ -1,8 +0,0 @@ -package fj; - -/** - * Created by MarkPerry on 22/01/2015. - */ -public abstract class F3W implements F3 { - -} diff --git a/core/src/main/java/fj/F4Functions.java b/core/src/main/java/fj/F4Functions.java deleted file mode 100644 index cb353764..00000000 --- a/core/src/main/java/fj/F4Functions.java +++ /dev/null @@ -1,21 +0,0 @@ -package fj; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public final class F4Functions { - - private F4Functions() { - } - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - public static F3 f(final F4 f, final A a) { - return (b, c, d) -> f.f(a, b, c, d); - } - -} diff --git a/core/src/main/java/fj/F4W.java b/core/src/main/java/fj/F4W.java deleted file mode 100644 index 3cc92e55..00000000 --- a/core/src/main/java/fj/F4W.java +++ /dev/null @@ -1,9 +0,0 @@ -package fj; - -/** - * Created by MarkPerry on 22/01/2015. - */ -public abstract class F4W implements F4 { - - -} diff --git a/core/src/main/java/fj/F5Functions.java b/core/src/main/java/fj/F5Functions.java deleted file mode 100644 index c39b228f..00000000 --- a/core/src/main/java/fj/F5Functions.java +++ /dev/null @@ -1,21 +0,0 @@ -package fj; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public final class F5Functions { - - private F5Functions() { - } - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - public static F4 f(final F5 f, final A a) { - return (b, c, d, e) -> f.f(a, b, c, d, e); - } - -} diff --git a/core/src/main/java/fj/F5W.java b/core/src/main/java/fj/F5W.java deleted file mode 100644 index 5fa407cd..00000000 --- a/core/src/main/java/fj/F5W.java +++ /dev/null @@ -1,9 +0,0 @@ -package fj; - -/** - * Created by MarkPerry on 22/01/2015. - */ -public abstract class F5W implements F5 { - - -} diff --git a/core/src/main/java/fj/F6Functions.java b/core/src/main/java/fj/F6Functions.java deleted file mode 100644 index 320b95f4..00000000 --- a/core/src/main/java/fj/F6Functions.java +++ /dev/null @@ -1,22 +0,0 @@ -package fj; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public final class F6Functions { - - private F6Functions() { - } - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - public static F5 f(final F6 func, final A a) { - return (b, c, d, e, f) -> func.f(a, b, c, d, e, f); - } - - -} diff --git a/core/src/main/java/fj/F6W.java b/core/src/main/java/fj/F6W.java deleted file mode 100644 index adaf723f..00000000 --- a/core/src/main/java/fj/F6W.java +++ /dev/null @@ -1,9 +0,0 @@ -package fj; - -/** - * Created by MarkPerry on 22/01/2015. - */ -public abstract class F6W implements F6 { - - -} diff --git a/core/src/main/java/fj/F7Functions.java b/core/src/main/java/fj/F7Functions.java deleted file mode 100644 index e583ba4b..00000000 --- a/core/src/main/java/fj/F7Functions.java +++ /dev/null @@ -1,22 +0,0 @@ -package fj; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public final class F7Functions { - - private F7Functions() { - } - - /** - * Partial application. - * - * @param a The A to which to apply this function. - * @return The function partially applied to the given argument. - */ - public static F6 f(final F7 func, final A a) { - return (b, c, d, e, f, g) -> func.f(a, b, c, d, e, f, g); - } - - -} diff --git a/core/src/main/java/fj/F7W.java b/core/src/main/java/fj/F7W.java deleted file mode 100644 index fdcec3d5..00000000 --- a/core/src/main/java/fj/F7W.java +++ /dev/null @@ -1,9 +0,0 @@ -package fj; - -/** - * Created by MarkPerry on 22/01/2015. - */ -public abstract class F7W implements F7 { - - -} diff --git a/core/src/main/java/fj/F8Functions.java b/core/src/main/java/fj/F8Functions.java deleted file mode 100644 index 8b9883a3..00000000 --- a/core/src/main/java/fj/F8Functions.java +++ /dev/null @@ -1,20 +0,0 @@ -package fj; - -/** - * Created by MarkPerry on 6/04/2014. - */ -public final class F8Functions { - - private F8Functions() { - } - - /** - * Partial application. - * - * @return The function partially applied to the given argument. - */ - public static F7 f(final F8 func, final A a) { - return (b, c, d, e, f, g, h) -> func.f(a, b, c, d, e, f, g, h); - } - -} diff --git a/core/src/main/java/fj/F8W.java b/core/src/main/java/fj/F8W.java deleted file mode 100644 index d0a9f861..00000000 --- a/core/src/main/java/fj/F8W.java +++ /dev/null @@ -1,9 +0,0 @@ -package fj; - -/** - * Created by MarkPerry on 22/01/2015. - */ -public abstract class F8W implements F8 { - - -} diff --git a/core/src/test/java/fj/FFunctionsTest.java b/core/src/test/java/fj/FFunctionsTest.java index e0df9164..b235ff67 100644 --- a/core/src/test/java/fj/FFunctionsTest.java +++ b/core/src/test/java/fj/FFunctionsTest.java @@ -7,23 +7,6 @@ import static org.junit.Assert.*; public class FFunctionsTest { - @Test - public void testApply() { - F8 f8 = - (i1, i2, i3, i4, i5, i6, i7, i8) -> - i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8; - F7 f7 = F8Functions.f(f8, 8); - F6 f6 = - F7Functions.f(f7, 7); - F5 f5 = F6Functions.f(f6, 6); - F4 f4 = F5Functions.f(f5, 5); - F3 f3 = F4Functions.f(f4, 4); - F2 f2 = F3Functions.f(f3, 3); - F f1 = f2.f(2); - assertThat(f1.f(1), is(36)); - } @Test public void testTreeK() { From 202f88c5eb051fe74cfff9ff71952058f47d936a Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 2 Apr 2021 00:31:15 +1000 Subject: [PATCH 128/173] Added functional interface annotations to functions --- core/src/main/java/fj/F.java | 1 + core/src/main/java/fj/F0.java | 1 + core/src/main/java/fj/F2.java | 1 + core/src/main/java/fj/F3.java | 1 + core/src/main/java/fj/F4.java | 1 + core/src/main/java/fj/F5.java | 1 + core/src/main/java/fj/F6.java | 1 + core/src/main/java/fj/F7.java | 1 + core/src/main/java/fj/F8.java | 1 + 9 files changed, 9 insertions(+) diff --git a/core/src/main/java/fj/F.java b/core/src/main/java/fj/F.java index d2208236..45cec4fe 100644 --- a/core/src/main/java/fj/F.java +++ b/core/src/main/java/fj/F.java @@ -21,6 +21,7 @@ * * @version %build.number% */ +@FunctionalInterface public interface F extends Function { /** * Transform A to B. diff --git a/core/src/main/java/fj/F0.java b/core/src/main/java/fj/F0.java index eab82a01..14c00ca2 100644 --- a/core/src/main/java/fj/F0.java +++ b/core/src/main/java/fj/F0.java @@ -3,6 +3,7 @@ /** * Created by MarkPerry on 21/01/2015. */ +@FunctionalInterface public interface F0 { A f(); diff --git a/core/src/main/java/fj/F2.java b/core/src/main/java/fj/F2.java index ba0e6f2f..4f7c306d 100644 --- a/core/src/main/java/fj/F2.java +++ b/core/src/main/java/fj/F2.java @@ -18,6 +18,7 @@ * * @version %build.number% */ +@FunctionalInterface public interface F2 extends BiFunction { /** * Transform A and B to C. diff --git a/core/src/main/java/fj/F3.java b/core/src/main/java/fj/F3.java index 540afdaa..b8c4dcf3 100644 --- a/core/src/main/java/fj/F3.java +++ b/core/src/main/java/fj/F3.java @@ -6,6 +6,7 @@ * * @version %build.number% */ +@FunctionalInterface public interface F3 { /** * Transform A, B and C to D. diff --git a/core/src/main/java/fj/F4.java b/core/src/main/java/fj/F4.java index 84dcfafa..d63f03a7 100644 --- a/core/src/main/java/fj/F4.java +++ b/core/src/main/java/fj/F4.java @@ -6,6 +6,7 @@ * * @version %build.number% */ +@FunctionalInterface public interface F4 { /** * Transform A, B, C and D to E. diff --git a/core/src/main/java/fj/F5.java b/core/src/main/java/fj/F5.java index 5fea676b..f63ae797 100644 --- a/core/src/main/java/fj/F5.java +++ b/core/src/main/java/fj/F5.java @@ -7,6 +7,7 @@ * * @version %build.number% */ +@FunctionalInterface public interface F5 { /** * Transform A, B, C, D and E to diff --git a/core/src/main/java/fj/F6.java b/core/src/main/java/fj/F6.java index 20101cf7..558c17fc 100644 --- a/core/src/main/java/fj/F6.java +++ b/core/src/main/java/fj/F6.java @@ -7,6 +7,7 @@ * * @version %build.number% */ +@FunctionalInterface public interface F6 { /** * Transform A, B, C, D, E and diff --git a/core/src/main/java/fj/F7.java b/core/src/main/java/fj/F7.java index 5a3ea1ef..05caefb4 100644 --- a/core/src/main/java/fj/F7.java +++ b/core/src/main/java/fj/F7.java @@ -7,6 +7,7 @@ * * @version %build.number% */ +@FunctionalInterface public interface F7 { /** * Transform A, B, C, D, E, diff --git a/core/src/main/java/fj/F8.java b/core/src/main/java/fj/F8.java index 8b986456..5aa103f2 100644 --- a/core/src/main/java/fj/F8.java +++ b/core/src/main/java/fj/F8.java @@ -7,6 +7,7 @@ * * @version %build.number% */ +@FunctionalInterface public interface F8 { /** * Transform A, B, C, D, E, From 2ee2950ab391a7213b01d9e0f6a81473e8b4c76e Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 2 Apr 2021 01:37:55 +1000 Subject: [PATCH 129/173] Removed IOW --- core/src/main/java/fj/data/IO.java | 28 ++++++++++++ core/src/main/java/fj/data/IOW.java | 45 ------------------- core/src/main/java/fj/data/SafeIO.java | 1 + demo/src/main/java/fj/demo/IOWalkthrough.java | 3 +- 4 files changed, 30 insertions(+), 47 deletions(-) delete mode 100644 core/src/main/java/fj/data/IOW.java diff --git a/core/src/main/java/fj/data/IO.java b/core/src/main/java/fj/data/IO.java index ee27bdb7..992abaf2 100644 --- a/core/src/main/java/fj/data/IO.java +++ b/core/src/main/java/fj/data/IO.java @@ -4,6 +4,9 @@ * Created by MarkPerry on 19/06/2014. */ +import fj.F; +import fj.Unit; + import java.io.IOException; /** @@ -13,8 +16,33 @@ * * @param the type of the result produced by the IO */ +@FunctionalInterface public interface IO { A run() throws IOException; + default SafeIO> safe() { + return IOFunctions.toSafeValidation(this); + } + + default IO map(F f) { + return IOFunctions.map(this, f); + } + + default IO bind(F> f) { + return IOFunctions.bind(this, f); + } + + default IO append(IO iob) { + return IOFunctions.append(this, iob); + } + + public static IO getContents() { + return () -> IOFunctions.getContents().run(); + } + + public static IO interact(F f) { + return () -> IOFunctions.interact(f).run(); + } + } diff --git a/core/src/main/java/fj/data/IOW.java b/core/src/main/java/fj/data/IOW.java deleted file mode 100644 index 859c9cdb..00000000 --- a/core/src/main/java/fj/data/IOW.java +++ /dev/null @@ -1,45 +0,0 @@ -package fj.data; - -import fj.F; -import fj.Unit; - -import java.io.IOException; - -/** - * Created by MarkPerry on 9/06/2015. - */ -public final class IOW implements IO { - - private final IO io; - - private IOW(IO in) { - io = in; - } - - public static IOW lift(IO io) { - return new IOW<>(io); - } - - @Override - public A run() throws IOException { - return io.run(); - } - - public SafeIO> safe() { - return IOFunctions.toSafeValidation(io); - } - - public IOW map(F f) { return lift(IOFunctions.map(io, f)); } - - public IOW bind(F> f) { return lift(IOFunctions.bind(io, f)); } - - public IOW append(IO iob) { return lift(IOFunctions.append(io, iob)); } - - public static IOW getContents() { - return lift(() -> IOFunctions.getContents().run()); - } - - public static IOW interact(F f) { - return lift(() -> IOFunctions.interact(f).run()); - } -} diff --git a/core/src/main/java/fj/data/SafeIO.java b/core/src/main/java/fj/data/SafeIO.java index ef4c608c..11fdc31a 100644 --- a/core/src/main/java/fj/data/SafeIO.java +++ b/core/src/main/java/fj/data/SafeIO.java @@ -3,6 +3,7 @@ /** * Created by MarkPerry on 3/07/2014. */ +@FunctionalInterface public interface SafeIO extends IO { @Override diff --git a/demo/src/main/java/fj/demo/IOWalkthrough.java b/demo/src/main/java/fj/demo/IOWalkthrough.java index 5c1c4663..2f1cb9eb 100644 --- a/demo/src/main/java/fj/demo/IOWalkthrough.java +++ b/demo/src/main/java/fj/demo/IOWalkthrough.java @@ -4,7 +4,6 @@ import fj.Unit; import fj.data.IO; import fj.data.IOFunctions; -import fj.data.IOW; import java.io.BufferedReader; import java.io.IOException; @@ -72,7 +71,7 @@ public static void main(String[] args) { // for composing IO values instead, the entire program can be written like so: F f = String::toUpperCase; - IOW.lift(stdoutPrintln("What's your name again?")) + stdoutPrintln("What's your name again?") .append(stdoutPrint("Name: ")) .append(stdinReadLine()) .bind(f.andThen(IOFunctions::stdoutPrintln)) From ef68abe9ecdf083cdfecc2b0c6c367073d0a4867 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Mon, 5 Apr 2021 12:18:51 +1000 Subject: [PATCH 130/173] Removed deprecated monoid methods --- core/src/main/java/fj/Monoid.java | 51 +------------------------------ 1 file changed, 1 insertion(+), 50 deletions(-) diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index 96d24b82..765026b3 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -466,20 +466,6 @@ public A append(A a1, A a2) { }); } - /** - * Constructs a monoid from the given semigroup and zero value, which must follow the monoidal laws. - * @deprecated since 4.7. Use {@link #monoidDef(Semigroup.Definition, Object)} or {@link Semigroup#monoid(Object)} instead. - * - * @param s The semigroup for the monoid. - * @param zero The zero for the monoid. - * @return A monoid instance that uses the given sun function and zero value. - */ - @Deprecated - public static Monoid monoid(final Semigroup s, final A zero) { - return s.monoid(zero); - } - - /** * A monoid that adds integers. */ @@ -528,19 +514,7 @@ public Integer multiply(int n, Integer integer) { return n <= 0 ? 1 : (int) StrictMath.pow(integer.doubleValue(), n); } }); - - /** - * @deprecated Since 4.7. Due to rounding errors, addition of doubles does not comply with monoid laws - */ - @Deprecated - public static final Monoid doubleAdditionMonoid = monoidDef((d1, d2) -> d1 + d2, 0.0); - - /** - * @deprecated Since 4.7. Due to rounding errors, multiplication of doubles does not comply with monoid laws - */ - @Deprecated - public static final Monoid doubleMultiplicationMonoid = monoidDef((d1, d2) -> d1 * d2, 1.0); - + /** * A monoid that adds big integers. */ @@ -889,17 +863,6 @@ public Option sum(F0>> oas) { }); } - /** - * A monoid for options. - * @deprecated since 4.7. Use {@link #firstOptionMonoid()}. - * - * @return A monoid for options. - */ - @Deprecated - public static Monoid> optionMonoid() { - return firstOptionMonoid(); - } - /** * A monoid for options that take the first available value. * @@ -1121,16 +1084,4 @@ public Set append(Set a1, Set a2) { }); } - /** - * A monoid for the maximum of elements with ordering o. - * @deprecated since 4.7. Use {@link Ord#maxMonoid(Object)} - * - * @param o An ordering of elements. - * @param zero The minimum element. - */ - @Deprecated - public static Monoid ordMaxMonoid(final Ord o, final A zero) { - return o.maxMonoid(zero); - } - } From 3b0f9b951f0a503b6326c1e40f48c43d6d381683 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Mon, 5 Apr 2021 13:27:22 +1000 Subject: [PATCH 131/173] Removed deprecated methods from core --- core/src/main/java/fj/Monoid.java | 2 +- core/src/main/java/fj/Ord.java | 38 ---------- core/src/main/java/fj/P.java | 7 ++ core/src/main/java/fj/P1.java | 31 +------- core/src/main/java/fj/Semigroup.java | 23 ------ core/src/main/java/fj/data/Array.java | 13 ---- core/src/main/java/fj/data/HashMap.java | 20 ----- core/src/main/java/fj/data/Java.java | 11 --- core/src/main/java/fj/data/List.java | 90 ----------------------- core/src/main/java/fj/data/Seq.java | 16 ---- core/src/main/java/fj/data/Set.java | 24 ------ core/src/main/java/fj/data/Stream.java | 53 ------------- core/src/main/java/fj/data/TreeMap.java | 20 +---- core/src/test/java/fj/data/ArrayTest.java | 1 + 14 files changed, 13 insertions(+), 336 deletions(-) diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index 765026b3..bc09250d 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -514,7 +514,7 @@ public Integer multiply(int n, Integer integer) { return n <= 0 ? 1 : (int) StrictMath.pow(integer.doubleValue(), n); } }); - + /** * A monoid that adds big integers. */ diff --git a/core/src/main/java/fj/Ord.java b/core/src/main/java/fj/Ord.java index eef729c4..ae4d3fda 100644 --- a/core/src/main/java/fj/Ord.java +++ b/core/src/main/java/fj/Ord.java @@ -687,44 +687,6 @@ public static > Ord comparableOrd() { return ordDef((a1, a2) -> Ordering.fromInt(a1.compareTo(a2))); } - /** - * An order instance that uses {@link Object#hashCode()} for computing the order and equality, - * thus objects returning the same hashCode are considered to be equals. - * This is not safe and therefore this method is deprecated. - * - * @return An order instance that is based on {@link Object#hashCode()}. - * - * @deprecated As of release 4.7. - */ - @Deprecated - public static Ord hashOrd() { - return ordDef(a -> { - int aHash = a.hashCode(); - return a2 -> Ordering.fromInt(Integer.valueOf(aHash).compareTo(a2.hashCode())); - }); - } - - /** - * An order instance that uses {@link Object#hashCode()} and {@link Object#equals} for computing - * the order and equality. First the hashCode is compared, if this is equal, objects are compared - * using {@link Object#equals}. - * WARNING: This ordering violate antisymmetry on hash collisions. - * - * @return An order instance that is based on {@link Object#hashCode()} and {@link Object#equals}. - * - * @deprecated As of release 4.7. - */ - @Deprecated - public static Ord hashEqualsOrd() { - return ordDef(a -> { - int aHash = a.hashCode(); - return a2 -> { - final int a2Hash = a2.hashCode(); - return aHash < a2Hash ? Ordering.LT : aHash == a2Hash && a.equals(a2) ? Ordering.EQ : Ordering.GT; - }; - }); - } - class OrdComparator implements Comparator { @Override public final int compare(A o1, A o2) { diff --git a/core/src/main/java/fj/P.java b/core/src/main/java/fj/P.java index 6d174ed7..587cdc9d 100644 --- a/core/src/main/java/fj/P.java +++ b/core/src/main/java/fj/P.java @@ -65,6 +65,13 @@ public static P1 softMemo(F0 f) { return new P1.SoftReferenceMemo<>(f); } + /** + * Convert a F0 into a P1, using weak call-by-need semantic using {@link #weakMemo(F0)}. + */ + public static P1 memo(F0 f) { + return weakMemo(f); + } + /** * Convert a F0 into a P1, using call-by-name semantic: * function f is evaluated at each call to {@link P1#_1()}. diff --git a/core/src/main/java/fj/P1.java b/core/src/main/java/fj/P1.java index 420b820b..810e484d 100644 --- a/core/src/main/java/fj/P1.java +++ b/core/src/main/java/fj/P1.java @@ -39,18 +39,6 @@ public static F, A> __1() { return P1::_1; } - /** - * Promote any function to a transformation between P1s. - * - * @deprecated As of release 4.5, use {@link #map_} - * @param f A function to promote to a transformation between P1s. - * @return A function promoted to operate on P1s. - */ - @Deprecated - public static F, P1> fmap(final F f) { - return map_(f); - } - /** * Promote any function to a transformation between P1s. * @@ -243,9 +231,8 @@ public final P1 map(final F f) { } /** - * @deprecated since 4.7. Use {@link P1#weakMemo()} instead. + * Wrap the memoized value into a WeakReference. */ - @Deprecated public final P1 memo() { return weakMemo(); } @@ -267,22 +254,6 @@ public final P1 memo() { */ public P1 softMemo() { return new SoftReferenceMemo<>(this); } - /** - * @deprecated since 4.7. Use {@link P#weakMemo(F0)} instead. - */ - @Deprecated - public static P1 memo(F f) { - return P.weakMemo(() -> f.f(unit())); - } - - /** - * @deprecated since 4.7. Use {@link P#weakMemo(F0)} instead. - */ - @Deprecated - public static P1 memo(F0 f) { - return P.weakMemo(f); - } - static final class Memo extends P1 { private volatile F0 fa; private A value; diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index 6fbc5fc6..1d767385 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -292,23 +292,11 @@ public static Semigroup semigroup(final F2 sum) { */ public static final Semigroup intAdditionSemigroup = intAdditionMonoid.semigroup(); - /** - * @deprecated Since 4.7. Due to rounding errors, addition of doubles does not comply with monoid laws - */ - @Deprecated - public static final Semigroup doubleAdditionSemigroup = semigroupDef((d1, d2) -> d1 + d2); - /** * A semigroup that multiplies integers. */ public static final Semigroup intMultiplicationSemigroup = intMultiplicationMonoid.semigroup(); - /** - * @deprecated Since 4.7. Due to rounding errors, addition of doubles does not comply with monoid laws - */ - @Deprecated - public static final Semigroup doubleMultiplicationSemigroup = semigroupDef((d1, d2) -> d1 * d2); - /** * A semigroup that yields the maximum of integers. */ @@ -520,17 +508,6 @@ public NonEmptyList sum(NonEmptyList nea, F0>> neas }); } - /** - * A semigroup for optional values. - * @deprecated since 4.7. Use {@link #firstOptionSemigroup()}. - * - * @return A semigroup for optional values. - */ - @Deprecated - public static Semigroup> optionSemigroup() { - return firstOptionSemigroup(); - } - /** * A semigroup for optional values that take the first available value. * diff --git a/core/src/main/java/fj/data/Array.java b/core/src/main/java/fj/data/Array.java index 7e937200..fb245f3f 100755 --- a/core/src/main/java/fj/data/Array.java +++ b/core/src/main/java/fj/data/Array.java @@ -130,19 +130,6 @@ public Object[] array() { return copyOf(a, a.length); } - /** - * To be removed in future release: - * affectation of the result of this method to a non generic array - * will result in runtime error (ClassCastException). - * - * @deprecated As of release 4.6, use {@link #array(Class)}. - */ - @SuppressWarnings("unchecked") - @Deprecated - public A[] toJavaArray() { - return (A[]) array(); - } - /** * Returns an option projection of this array; None if empty, or the first element in * Some. diff --git a/core/src/main/java/fj/data/HashMap.java b/core/src/main/java/fj/data/HashMap.java index 400cf415..455c529c 100755 --- a/core/src/main/java/fj/data/HashMap.java +++ b/core/src/main/java/fj/data/HashMap.java @@ -325,16 +325,6 @@ public java.util.Map toMap() { return result; } - /** - * Converts the Iterable to a HashMap - * - * @deprecated As of release 4.5, use {@link #iterableHashMap(Iterable)} - */ - @Deprecated - public static HashMap from(final Iterable> entries) { - return iterableHashMap(entries); - } - public static HashMap fromMap(java.util.Map map) { return fromMap(Equal.anyEqual(), Hash.anyHash(), map); } @@ -347,16 +337,6 @@ public static HashMap fromMap(Equal eq, Hash h, java.util.Map return m; } - /** - * Converts the Iterable to a HashMap - * - * @deprecated As of release 4.5, use {@link #iterableHashMap} - */ - @Deprecated - public static HashMap from(final Iterable> entries, final Equal equal, final Hash hash) { - return iterableHashMap(equal, hash, entries); - } - /** * Converts the Iterable to a HashMap */ diff --git a/core/src/main/java/fj/data/Java.java b/core/src/main/java/fj/data/Java.java index ae7fcf6a..8691c09d 100644 --- a/core/src/main/java/fj/data/Java.java +++ b/core/src/main/java/fj/data/Java.java @@ -1414,17 +1414,6 @@ public static F, List> ArrayList_List() { // END ArrayList -> - /** - * A function that converts Java lists to lists. - * @deprecated As of 4.3, use {@link #JavaList_List} - * - * @return A function that converts Java lists to lists. - */ - @Deprecated - public static F, List> JUList_List() { - return Java::JavaList_List; - } - public static F, List> JavaList_List() { return Java::JavaList_List; } diff --git a/core/src/main/java/fj/data/List.java b/core/src/main/java/fj/data/List.java index 5b5fff67..447f76a5 100644 --- a/core/src/main/java/fj/data/List.java +++ b/core/src/main/java/fj/data/List.java @@ -86,19 +86,6 @@ public final boolean isNotEmpty() { return this instanceof Cons; } - /** - * Performs a reduction on this list using the given arguments. - * @deprecated As of release 4.5, use {@link #uncons} - * - * @param nil The value to return if this list is empty. - * @param cons The function to apply to the head and tail of this list if it is not empty. - * @return A reduction on this list. - */ - @Deprecated - public final B list(final B nil, final F, B>> cons) { - return uncons(uncurryF2(cons), nil); - } - public final B uncons(final F2, B> cons, final B nil) { return isEmpty() ? nil : cons.f(head(), tail()); } @@ -123,18 +110,6 @@ public final List orTail(final F0> as) { return isEmpty() ? as.f() : tail(); } - /** - * Returns an option projection of this list; None if empty, or the first element in - * Some. Equivalent to {@link #headOption()}. - * @deprecated As of release 4.5, use {@link #headOption()} - * @return An option projection of this list. - */ - @Deprecated - public final Option toOption() { - return headOption(); - - } - /** * Returns the head of the list, if any. Equivalent to {@link #toOption()} . * @@ -185,19 +160,6 @@ public final Object[] toArrayObject() { return a; } - /** - * To be removed in future release: - * affectation of the result of this method to a non generic array - * will result in runtime error (ClassCastException). - * - * @deprecated As of release 4.6, use {@link #array(Class)}. - */ - @SuppressWarnings("unchecked") - @Deprecated - public final A[] toJavaArray() { - return (A[]) toArrayObject(); - } - /** * Returns a array projection of this list. * @@ -1529,21 +1491,6 @@ public final A mode(final Ord o) { return sort(o).group(o.equal()).maximum(intOrd.contramap(List.length_())).head(); } - /** - * Groups the elements of this list by a given keyFunction into a {@link TreeMap}. - * The ordering of the keys is determined by {@link fj.Ord#hashOrd()} (ie. Object#hasCode). - * This is not safe and therefore this method is deprecated. - * - * @param keyFunction The function to select the keys for the map. - * @return A TreeMap containing the keys with the accumulated list of matched elements. - * - * @deprecated As of release 4.7, use {@link #groupBy(F, Ord)} - */ - @Deprecated - public final TreeMap> groupBy(final F keyFunction) { - return groupBy(keyFunction, Ord.hashOrd()); - } - /** * Groups the elements of this list by a given keyFunction into a {@link TreeMap}. * @@ -1555,25 +1502,6 @@ public final TreeMap> groupBy(final F keyFunction, final Or return groupBy(keyFunction, identity(), keyOrd); } - /** - * Groups the elements of this list by a given keyFunction into a {@link TreeMap} and transforms - * the matching elements with the given valueFunction. The ordering of the keys is determined by - * {@link fj.Ord#hashOrd()} (ie. Object#hasCode). - * This is not safe and therefore this method is deprecated. - * - * @param keyFunction The function to select the keys for the map. - * @param valueFunction The function to apply on each matching value. - * @return A TreeMap containing the keys with the accumulated list of matched and mapped elements. - * - * @deprecated As of release 4.7, use {@link #groupBy(F, F, Ord)} - */ - @Deprecated - public final TreeMap> groupBy( - final F keyFunction, - final F valueFunction) { - return this.groupBy(keyFunction, valueFunction, Ord.hashOrd()); - } - /** * Groups the elements of this list by a given keyFunction into a {@link TreeMap} and transforms * the matching elements with the given valueFunction. The ordering of the keys is determined by @@ -1821,24 +1749,6 @@ public static List arrayList(final A... as) { return Array.array(as).toList(); } - /** - * Constructs a list from the given Iterable. - * @deprecated As of release 4.5, use {@link #iterableList(Iterable)} - */ - @Deprecated - public static List list(final Iterable i) { - return iterableList(i); - } - - /** - * Constructs a list from the given Iterator. - * @deprecated As of release 4.5, use {@link #iteratorList(Iterator)} - */ - @Deprecated - public static List list(final Iterator it) { - return iteratorList(it); - } - /** * Constructs a list from the given Iterator. */ diff --git a/core/src/main/java/fj/data/Seq.java b/core/src/main/java/fj/data/Seq.java index 3bcb7033..59a46df7 100644 --- a/core/src/main/java/fj/data/Seq.java +++ b/core/src/main/java/fj/data/Seq.java @@ -78,25 +78,9 @@ public static Seq single(final A a) { /** * Constructs a sequence from the given list. * - * @deprecated As of release 4.5, use {@link #listSeq(List)} - * - * @param list The list to create the sequence from. - * @return A sequence with the given elements in the list. - */ - @Deprecated - public static Seq seq(final List list) { - return iterableSeq(list); - } - - /** - * Constructs a sequence from the given list. - * - * @deprecated As of release 4.5, use {@link #iterableSeq} - * * @param list The list to create the sequence from. * @return A sequence with the elements of the list. */ - @Deprecated public static Seq listSeq(final List list) { return iterableSeq(list); } diff --git a/core/src/main/java/fj/data/Set.java b/core/src/main/java/fj/data/Set.java index 15207d83..4c0aeaa0 100644 --- a/core/src/main/java/fj/data/Set.java +++ b/core/src/main/java/fj/data/Set.java @@ -713,28 +713,4 @@ public static Set arraySet(final Ord o, final A...as) { return arraySet(o, as); } - /** - * Constructs a set from the list. - * - * @deprecated As of release 4.5, use {@link #iterableSet} - * - * @param o An order for the elements of the new set. - * @param list The elements to add to a set. - * @return A new set containing the elements of the given list. - */ - @Deprecated - public static Set set(final Ord o, List list) { - return iterableSet(o, list); - } - - /** - * Constructs a set from the list. - * - * @deprecated As of release 4.5, use {@link #iterableSet} - */ - @Deprecated - public static Set fromList(final Ord o, List list) { - return iterableSet(o, list); - } - } diff --git a/core/src/main/java/fj/data/Stream.java b/core/src/main/java/fj/data/Stream.java index e05e26bf..bcf87dcf 100644 --- a/core/src/main/java/fj/data/Stream.java +++ b/core/src/main/java/fj/data/Stream.java @@ -89,20 +89,6 @@ public final boolean isNotEmpty() { return this instanceof Cons; } - /** - * Performs a reduction on this stream using the given arguments. Equivalent to {@link #uncons}. - * - * @deprecated As of release 4.5, use {@link #uncons} - * - * @param nil The value to return if this stream is empty. - * @param cons The function to apply to the head and tail of this stream if it is not empty. - * @return A reduction on this stream. - */ - @Deprecated - public final B stream(final B nil, final F>, B>> cons) { - return uncons(nil, cons); - } - /** * Performs a reduction on this stream using the given arguments. * @@ -712,26 +698,6 @@ public static Stream range(final int from, final long to) { return arrayStream(as); } - /** - * Constructs a stream with the given elements in the Iterable. Equivalent to {@link #iterableStream(Iterable)} . - * - * @deprecated As of release 4.5, use {@link #iterableStream(Iterable)} - */ - @Deprecated - public static Stream stream(Iterable it) { - return iterableStream(it); - } - - /** - * Constructs a stream with the given elements in the Iterator. Equivalent to {@link #iteratorStream(Iterator)} . - * - * @deprecated As of release 4.5, use {@link #iteratorStream(Iterator)} - */ - @Deprecated - public static Stream stream(final Iterator it) { - return iteratorStream(it); - } - /** * Constructs a stream with the given elements in the Iterator. */ @@ -916,25 +882,6 @@ public final Option toOption() { return isEmpty() ? Option.none() : some(head()); } - /** - * To be removed in future release: - * affectation of the result of this method to a non generic array - * will result in runtime error (ClassCastException). - * - * @deprecated As of release 4.6, use {@link #array(Class)}. - */ - @Deprecated - public final A[] toJavaArray() { - @SuppressWarnings("unchecked") - final A[] array = (A[]) new Object[length()]; - int i = 0; - for (A a: this) { - array[i] = a; - i++; - } - return array; - } - /** * Returns a list projection of this stream. * diff --git a/core/src/main/java/fj/data/TreeMap.java b/core/src/main/java/fj/data/TreeMap.java index 38607dbc..e1af4d91 100644 --- a/core/src/main/java/fj/data/TreeMap.java +++ b/core/src/main/java/fj/data/TreeMap.java @@ -69,20 +69,6 @@ public String toString() { return arrayTreeMap(keyOrd, p2s); } - /** - * Constructs a tree map from the given elements. - * - * @deprecated As of release 4.5, use {@link #iterableTreeMap(Ord, Iterable)} - * - * @param keyOrd An order for the keys of the tree map. - * @param list The elements to construct the tree map with. - * @return a TreeMap with the given elements. - */ - @Deprecated - public static TreeMap treeMap(final Ord keyOrd, final List> list) { - return iterableTreeMap(keyOrd, list); - } - /** * Constructs a tree map from the given elements. * @@ -313,12 +299,12 @@ public P3, Option, Set> split(Ord ord, final K k) { } /** - * Internal construction of a TreeMap from the given set. + * Constructs a TreeMap from the given set. * @param ord An order for the keys of the tree map. * @param s The elements to construct the tree map with. * @return a TreeMap with the given elements. */ - private static TreeMap treeMap(Ord ord, Set>> s) { + public static TreeMap setTreeMap(Ord ord, Set>> s) { TreeMap empty = TreeMap.empty(ord); TreeMap tree = s.toList().foldLeft((tm, p2) -> { Option opt = p2._2(); @@ -347,7 +333,7 @@ private static TreeMap treeMap(Ord ord, Set>> s) public P3, Option, TreeMap> splitLookup(final K k) { P3>>, Option>>, Set>>> p3 = tree.split(p(k, get(k))); Ord o = tree.ord().contramap(k2 -> p(k2, Option.none())); - return p(treeMap(o, p3._1()), get(k), treeMap(o, p3._3())); + return p(setTreeMap(o, p3._1()), get(k), setTreeMap(o, p3._3())); } /** diff --git a/core/src/test/java/fj/data/ArrayTest.java b/core/src/test/java/fj/data/ArrayTest.java index 886031b3..883fc835 100644 --- a/core/src/test/java/fj/data/ArrayTest.java +++ b/core/src/test/java/fj/data/ArrayTest.java @@ -1,5 +1,6 @@ package fj.data; +import org.hamcrest.CoreMatchers; import org.junit.Test; import static org.hamcrest.CoreMatchers.equalTo; From e6234b801de460ff759aafeb4674edaf3c32c503 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Mon, 5 Apr 2021 15:54:41 +1000 Subject: [PATCH 132/173] Removed deprecated methods from quickcheck --- quickcheck/src/main/java/fj/test/Gen.java | 33 ------------------ quickcheck/src/main/java/fj/test/Rand.java | 39 +++------------------- 2 files changed, 5 insertions(+), 67 deletions(-) diff --git a/quickcheck/src/main/java/fj/test/Gen.java b/quickcheck/src/main/java/fj/test/Gen.java index dabaf9a6..8fe1e6f5 100644 --- a/quickcheck/src/main/java/fj/test/Gen.java +++ b/quickcheck/src/main/java/fj/test/Gen.java @@ -536,24 +536,6 @@ public static Gen pickOne(List as) { return wordOf(1, as).map(List::head); } - /** - * Returns a generator of lists that picks the given number of elements from the given list. If - * the given number is less than zero or greater than the length of the given list, then the - * returned generator will never produce a value. - *

      - * Note: pick is synonymous with combinationOf - * - * @deprecated As of release 4.6, use {@link #combinationOf} - * - * @param n The number of elements to pick from the given list. - * @param as The list from which to pick elements. - * @return A generator of lists that picks the given number of elements from the given list. - */ - @Deprecated - public static Gen> pick(int n, List as) { - return combinationOf(n, as); - } - /** * Returns a generator of lists that picks the given number of elements from the given list. The selection is * a combination without replacement of elements from the given list, i.e. @@ -678,21 +660,6 @@ private static Gen> pick(Gen> indexesGen, Array as) indexes.foldLeft((acc, index) -> cons(as.get(index), acc), List.nil()).reverse()); } - /** - * Returns a generator of lists that produces some of the values of the given list. - *

      - * Note: someOf is synonymous with someCombinationOf - * - * @deprecated As of release 4.6, use {@link #someCombinationOf} - * - * @param as The list from which to pick values. - * @return A generator of lists that produces some of the values of the given list. - */ - @Deprecated - public static Gen> someOf(List as) { - return someCombinationOf(as); - } - /** * Returns a generator of lists that produces some of the values of the given list. The selection is * a combination without replacement of elements from the given list, i.e. diff --git a/quickcheck/src/main/java/fj/test/Rand.java b/quickcheck/src/main/java/fj/test/Rand.java index b622dfe7..e47aa8a8 100644 --- a/quickcheck/src/main/java/fj/test/Rand.java +++ b/quickcheck/src/main/java/fj/test/Rand.java @@ -5,7 +5,6 @@ import java.util.Random; -import static fj.data.Option.none; import static fj.data.Option.some; import static java.lang.Math.max; import static java.lang.Math.min; @@ -18,18 +17,16 @@ public final class Rand { private final F, F>> f; private final F, F>> g; - - // TODO Change to F when rand(f,g) is removed - private final Option> optOnReseed; + private final F onReseed; private Rand( F, F>> f, F, F>> g, - Option> optOnReseed) { + F onReseed) { this.f = f; this.g = g; - this.optOnReseed = optOnReseed; + this.onReseed = onReseed; } /** @@ -88,33 +85,7 @@ public double choose(final double from, final double to) { * @return A random generator with the given seed. */ public Rand reseed(long seed) { - return optOnReseed.option( - () -> { - throw new IllegalStateException("reseed() called on a Rand created with deprecated rand() method"); - }, - onReseed -> onReseed.f(seed)); - } - - /** - * Constructs a random generator from the given functions that supply a range to produce a - * result. - *

      - * Calling {@link #reseed(long)} on an instance returned from this method will - * result in an exception being thrown. - * - * @deprecated As of release 4.6, use {@link #rand(F, F, F)}. - * - * @param f The integer random generator. - * @param g The floating-point random generator. - * @return A random generator from the given functions that supply a range to produce a result. - */ - // TODO Change Option> optOnReseed to F onReseed when removing this method - @Deprecated - public static Rand rand( - F, F>> f, - F, F>> g) { - - return new Rand(f, g, none()); + return onReseed.f(seed); } /** @@ -131,7 +102,7 @@ public static Rand rand( F, F>> g, F onReseed) { - return new Rand(f, g, some(onReseed)); + return new Rand(f, g, onReseed); } /** From 4859b75a3837c4c772c65c46596f388e7c1627b3 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Tue, 6 Apr 2021 01:53:30 +1000 Subject: [PATCH 133/173] Added partial Either3 implementation --- core/src/main/java/fj/Equal.java | 8 + core/src/main/java/fj/Hash.java | 7 +- core/src/main/java/fj/Show.java | 10 + core/src/main/java/fj/data/Either3.java | 226 ++++++++++++++++++++ core/src/test/java/fj/data/Either3Test.java | 6 + 5 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/fj/data/Either3.java create mode 100644 core/src/test/java/fj/data/Either3Test.java diff --git a/core/src/main/java/fj/Equal.java b/core/src/main/java/fj/Equal.java index 5cd31aeb..9179cc43 100644 --- a/core/src/main/java/fj/Equal.java +++ b/core/src/main/java/fj/Equal.java @@ -357,6 +357,14 @@ public static Equal> eitherEqual(final Equal ea, final Eq )); } + public static Equal> either3Equal(Equal ea, Equal eb, Equal ec) { + return equalDef((e1, e2) -> + optionEqual(ea).eq(e1.leftOption(), e2.leftOption()) && + optionEqual(eb).eq(e1.middleOption(), e2.middleOption()) && + optionEqual(ec).eq(e1.rightOption(), e2.rightOption()) + ); + } + public static Equal> resultEqual(final Equal ea, final Equal ei) { Definition eaDef = ea.def; Definition eiDef= ei.def; diff --git a/core/src/main/java/fj/Hash.java b/core/src/main/java/fj/Hash.java index 8d0a4e4c..703d96ee 100644 --- a/core/src/main/java/fj/Hash.java +++ b/core/src/main/java/fj/Hash.java @@ -163,7 +163,12 @@ public static Hash> eitherHash(final Hash ha, final Hash< return hash(e -> e.isLeft() ? ha.hash(e.left().value()) : hb.hash(e.right().value())); } - /** + public static Hash> either3Hash(final Hash ha, final Hash hb, final Hash hc) { + return hash(e -> e.either(a -> ha.hash(a), b -> hb.hash(b), c -> hc.hash(c))); + } + + + /** * A hash instance for the {@link Result} type. * * @param ha Hash the Result value. diff --git a/core/src/main/java/fj/Show.java b/core/src/main/java/fj/Show.java index cd57897b..62da5273 100644 --- a/core/src/main/java/fj/Show.java +++ b/core/src/main/java/fj/Show.java @@ -256,6 +256,16 @@ public static Show> eitherShow(final Show sa, final Show< fromString("Right(").append(sb.f.f(e.right().value())).append(single(')'))); } + public static Show> eitherShow(final Show sa, final Show sb, final Show sc) { + return show(e -> + e.either( + a -> fromString("Left(").append(sa.f.f(a)).append(single(')')), + b -> fromString("Middle(").append(sb.f.f(b)).append(single(')')), + c -> fromString("Right(").append(sc.f.f(c)).append(single(')')) + ) + ); + } + /** * A show instance for the {@link Result} type. * diff --git a/core/src/main/java/fj/data/Either3.java b/core/src/main/java/fj/data/Either3.java new file mode 100644 index 00000000..8c6b7344 --- /dev/null +++ b/core/src/main/java/fj/data/Either3.java @@ -0,0 +1,226 @@ +package fj.data; + +import fj.Equal; +import fj.F; +import fj.F0; +import fj.Hash; + +import static fj.data.Option.none; +import static fj.data.Option.some; + +public abstract class Either3 { + + private Either3() {} + + private static final class Left extends Either3 { + A a; + Left(A a) { + this.a = a; + } + + @Override + public boolean isLeft() { + return true; + } + + @Override + public boolean isMiddle() { + return false; + } + + @Override + public boolean isRight() { + return false; + } + + @Override + public D either(F fa, F fb, F fc) { + return fa.f(a); + } + } + + private static final class Middle extends Either3 { + B b; + Middle(B b) { + this.b = b; + } + + @Override + public boolean isLeft() { + return false; + } + + @Override + public boolean isMiddle() { + return true; + } + + @Override + public boolean isRight() { + return false; + } + + @Override + public D either(F fa, F fb, F fc) { + return fb.f(b); + } + } + + private static final class Right extends Either3 { + C c; + Right(C c) { + this.c = c; + } + + @Override + public boolean isLeft() { + return false; + } + + @Override + public boolean isMiddle() { + return false; + } + + @Override + public boolean isRight() { + return true; + } + + @Override + public D either(F fa, F fb, F fc) { + return fc.f(c); + } + + } + + public static final class LeftProjection { + private final Either3 e; + + private LeftProjection(final Either3 e) { + this.e = e; + } + + public Either3 apply(final Either3, B, C> e) { + return e.left().bind(this::map); + } + + public boolean exists(final F f) { + return e.either(a -> f.f(a), b -> false, c -> false); + } + + public Either3 bind(F> f) { + return e.either(a -> f.f(a), b -> middle(b), c -> right(c)); + } + + public Option> filter(final F f) { + return e.either(a -> f.f(a) ? some(left(a)) : none(), b -> none(), c -> none()); + } + + + public Either3 map(final F f) { + return e.either(a -> left(f.f(a)), b -> middle(b), c -> right(c)); + } + + } + + public static final class MiddleProjection { + private final Either3 e; + + private MiddleProjection(final Either3 e) { + this.e = e; + } + + public Either3 bind(F> f) { + return e.either(a -> left(a), b -> f.f(b), c -> right(c)); + } + + } + + public static final class RightProjection { + private final Either3 e; + + private RightProjection(final Either3 e) { + this.e = e; + } + + public Either3 bind(F> f) { + return e.either(a -> left(a), b -> middle(b), c -> f.f(c)); + } + } + + public static Either3 left(A a) { + return new Left<>(a); + } + + public static Either3 middle(B b) { + return new Middle<>(b); + } + + public static Either3 right(C c) { + return new Right<>(c); + } + + public abstract boolean isLeft(); + + public abstract boolean isMiddle(); + + public abstract boolean isRight(); + + public Either3 map3(F fl, F fm, F fr) { + return either( + a -> left(fl.f(a)), + b -> middle(fm.f(b)), + c -> right(fr.f(c)) + ); + } + + public abstract D either(F fa, F fb, F fc); + + public Either3 moveLeft() { + return either(a -> right(a), b -> left(b), c -> middle(c)); + } + + public Either3 moveRight() { + return either(a -> middle(a), b -> right(b), c -> left(c)); + } + + public Either3 swap() { + return either(a -> right(a), b -> middle(b), c -> left(c)); + } + + public Option leftOption() { + return either(a -> some(a), b -> none(), c -> none()); + } + + public Option middleOption() { + return either(a -> none(), b -> some(b), c -> none()); + } + + public Option rightOption() { + return either(a -> none(), b -> none(), c -> some(c)); + } + + public final LeftProjection left() { + return new LeftProjection<>(this); + } + + public final MiddleProjection middle() { + return new MiddleProjection<>(this); + } + + public final RightProjection right() { + return new RightProjection<>(this); + } + + @Override + public final boolean equals(Object other) { + return Equal.equals0(Either3.class, this, other, () -> Equal.either3Equal(Equal.anyEqual(), Equal.anyEqual(), Equal.anyEqual())); + } + + @Override + public final int hashCode() { + return Hash.either3Hash(Hash.anyHash(), Hash.anyHash(), Hash.anyHash()).hash(this); + } + +} diff --git a/core/src/test/java/fj/data/Either3Test.java b/core/src/test/java/fj/data/Either3Test.java new file mode 100644 index 00000000..1507bf41 --- /dev/null +++ b/core/src/test/java/fj/data/Either3Test.java @@ -0,0 +1,6 @@ +package fj.data; + +public final class Either3Test { + + +} From d2f777d7ee57b48fe8dfe71317ec0790d39c76ab Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Tue, 6 Apr 2021 13:37:52 +1000 Subject: [PATCH 134/173] Fixed javadoc on headOption. --- core/src/main/java/fj/data/List.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/fj/data/List.java b/core/src/main/java/fj/data/List.java index 447f76a5..9259900f 100644 --- a/core/src/main/java/fj/data/List.java +++ b/core/src/main/java/fj/data/List.java @@ -111,7 +111,7 @@ public final List orTail(final F0> as) { } /** - * Returns the head of the list, if any. Equivalent to {@link #toOption()} . + * Returns the head of the list, if any. * * @return The optional head of the list. */ From d3af2373fc02278ea76a11237b0c9d3c5fb5effa Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Tue, 6 Apr 2021 13:38:50 +1000 Subject: [PATCH 135/173] Implemented isLeft, isRight, isMiddle in terms of either. Added swap methods. --- core/src/main/java/fj/data/Either3.java | 75 ++++++++----------------- 1 file changed, 23 insertions(+), 52 deletions(-) diff --git a/core/src/main/java/fj/data/Either3.java b/core/src/main/java/fj/data/Either3.java index 8c6b7344..15b1f419 100644 --- a/core/src/main/java/fj/data/Either3.java +++ b/core/src/main/java/fj/data/Either3.java @@ -2,7 +2,6 @@ import fj.Equal; import fj.F; -import fj.F0; import fj.Hash; import static fj.data.Option.none; @@ -13,53 +12,26 @@ public abstract class Either3 { private Either3() {} private static final class Left extends Either3 { - A a; + private final A a; + Left(A a) { this.a = a; } - @Override - public boolean isLeft() { - return true; - } - - @Override - public boolean isMiddle() { - return false; - } - - @Override - public boolean isRight() { - return false; - } - @Override public D either(F fa, F fb, F fc) { return fa.f(a); } + } private static final class Middle extends Either3 { - B b; + private final B b; + Middle(B b) { this.b = b; } - @Override - public boolean isLeft() { - return false; - } - - @Override - public boolean isMiddle() { - return true; - } - - @Override - public boolean isRight() { - return false; - } - @Override public D either(F fa, F fb, F fc) { return fb.f(b); @@ -67,26 +39,11 @@ public D either(F fa, F fb, F fc) { } private static final class Right extends Either3 { - C c; + private final C c; Right(C c) { this.c = c; } - @Override - public boolean isLeft() { - return false; - } - - @Override - public boolean isMiddle() { - return false; - } - - @Override - public boolean isRight() { - return true; - } - @Override public D either(F fa, F fb, F fc) { return fc.f(c); @@ -161,11 +118,17 @@ public static Either3 right(C c) { return new Right<>(c); } - public abstract boolean isLeft(); + public boolean isLeft() { + return either(a -> true, b -> false, c -> false); + } - public abstract boolean isMiddle(); + public boolean isMiddle() { + return either(a -> false, b -> true, c -> false); + } - public abstract boolean isRight(); + public boolean isRight() { + return either(a -> false, b -> false, c -> true); + } public Either3 map3(F fl, F fm, F fr) { return either( @@ -189,6 +152,14 @@ public Either3 swap() { return either(a -> right(a), b -> middle(b), c -> left(c)); } + public Either3 swapLefts() { + return either(a -> middle(a), b -> left(b), c -> right(c)); + } + + public Either3 swapRights() { + return either(a -> left(a), b -> right(b), c -> middle(c)); + } + public Option leftOption() { return either(a -> some(a), b -> none(), c -> none()); } From ef79720aa0afc1fd679198e652d8d478984dff75 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Wed, 7 Apr 2021 21:39:52 +1000 Subject: [PATCH 136/173] Updated inheritance of interfaces to Java 8 interfaces --- core/src/main/java/fj/F0.java | 8 +++++++- core/src/main/java/fj/data/IO.java | 7 ++++++- core/src/main/java/fj/function/Effect0.java | 2 ++ core/src/main/java/fj/function/Effect1.java | 20 +++++++++++++++++++- core/src/main/java/fj/function/Effect2.java | 8 +++++++- 5 files changed, 41 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/fj/F0.java b/core/src/main/java/fj/F0.java index 14c00ca2..c260992f 100644 --- a/core/src/main/java/fj/F0.java +++ b/core/src/main/java/fj/F0.java @@ -1,11 +1,17 @@ package fj; +import java.util.function.Supplier; + /** * Created by MarkPerry on 21/01/2015. */ @FunctionalInterface -public interface F0 { +public interface F0 extends Supplier { A f(); + default A get() { + return f(); + } + } diff --git a/core/src/main/java/fj/data/IO.java b/core/src/main/java/fj/data/IO.java index 992abaf2..e2a94a38 100644 --- a/core/src/main/java/fj/data/IO.java +++ b/core/src/main/java/fj/data/IO.java @@ -6,6 +6,7 @@ import fj.F; import fj.Unit; +import fj.function.Try0; import java.io.IOException; @@ -17,10 +18,14 @@ * @param the type of the result produced by the IO */ @FunctionalInterface -public interface IO { +public interface IO extends Try0 { A run() throws IOException; + default A f() throws IOException { + return run(); + } + default SafeIO> safe() { return IOFunctions.toSafeValidation(this); } diff --git a/core/src/main/java/fj/function/Effect0.java b/core/src/main/java/fj/function/Effect0.java index c5d4fe50..57800eb0 100644 --- a/core/src/main/java/fj/function/Effect0.java +++ b/core/src/main/java/fj/function/Effect0.java @@ -1,5 +1,7 @@ package fj.function; +import fj.F0; + /** * Created by mperry on 28/08/2014. */ diff --git a/core/src/main/java/fj/function/Effect1.java b/core/src/main/java/fj/function/Effect1.java index 8c89b715..0cc35a39 100644 --- a/core/src/main/java/fj/function/Effect1.java +++ b/core/src/main/java/fj/function/Effect1.java @@ -1,10 +1,28 @@ package fj.function; +import fj.F; +import fj.Unit; + +import java.util.function.Consumer; + +import static fj.Unit.unit; + /** * Created by mperry on 28/08/2014. */ -public interface Effect1 { +public interface Effect1 extends Consumer { void f(A a); + default void accept(A a) { + f(a); + } + + default F toF() { + return a -> { + f(a); + return unit(); + }; + } + } diff --git a/core/src/main/java/fj/function/Effect2.java b/core/src/main/java/fj/function/Effect2.java index c0735043..40d8abd8 100644 --- a/core/src/main/java/fj/function/Effect2.java +++ b/core/src/main/java/fj/function/Effect2.java @@ -1,10 +1,16 @@ package fj.function; +import java.util.function.BiConsumer; + /** * Created by mperry on 28/08/2014. */ -public interface Effect2 { +public interface Effect2 extends BiConsumer { void f(A a, B b); + default void accept(A a, B b) { + f(a, b); + } + } From 7d3f6130c4ab6cafe7fe81e5c21ceacdb8a8b375 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Wed, 7 Apr 2021 21:40:49 +1000 Subject: [PATCH 137/173] Implemented left and middle projections of Either3. --- core/src/main/java/fj/data/Either3.java | 286 +++++++++++++++++++++++- 1 file changed, 279 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/fj/data/Either3.java b/core/src/main/java/fj/data/Either3.java index 15b1f419..0c7b7371 100644 --- a/core/src/main/java/fj/data/Either3.java +++ b/core/src/main/java/fj/data/Either3.java @@ -1,9 +1,16 @@ package fj.data; -import fj.Equal; -import fj.F; -import fj.Hash; +import fj.*; +import fj.function.Effect1; +import java.util.Collection; +import java.util.Iterator; + +import static fj.Function.identity; +import static fj.P.p; +import static fj.Unit.unit; +import static fj.data.Array.mkArray; +import static fj.data.List.*; import static fj.data.Option.none; import static fj.data.Option.some; @@ -49,6 +56,8 @@ public D either(F fa, F fb, F fc) { return fc.f(c); } + + } public static final class LeftProjection { @@ -62,23 +71,121 @@ public Either3 apply(final Either3, B, C> e) { return e.left().bind(this::map); } - public boolean exists(final F f) { - return e.either(a -> f.f(a), b -> false, c -> false); - } - public Either3 bind(F> f) { return e.either(a -> f.f(a), b -> middle(b), c -> right(c)); } + public Either3 either() { + return e; + } + + public boolean exists(final F f) { + return e.either(a -> f.f(a), b -> false, c -> false); + } + public Option> filter(final F f) { return e.either(a -> f.f(a) ? some(left(a)) : none(), b -> none(), c -> none()); } + public boolean forall(final F f) { + return e.either(a -> f.f(a), b -> true, c -> true); + } + + public Unit foreach(final F f) { + return e.either(a -> f.f(a), b -> unit(), c -> unit()); + } + + public void foreachDoEffect(final Effect1 f) { + e.either(a -> f.toF().f(a), b -> unit(), c -> unit()); + } + + public Iterator iterator() { + return toList().iterator(); + } public Either3 map(final F f) { return e.either(a -> left(f.f(a)), b -> middle(b), c -> right(c)); } + public A orValue(final A value) { + return orValue(() -> value); + } + + public A orValue(final F0 f) { + return e.either(a -> a, b -> f.f(), c -> f.f()); + } + + public Either3 sequence(final Either3 e) { + return bind(Function.constant(e)); + } + + public Array toArray() { + return e.either( + a -> Array.single(a), + b -> Array.empty(), + c -> Array.empty() + ); + } + + public Collection toCollection() { + return toList().toCollection(); + } + + public List toList() { + return e.either(a -> single(a), b -> nil(), c -> nil()); + } + + public Option toOption() { + return e.either(a -> some(a), b -> none(), c -> none()); + } + + public Stream toStream() { + return e.either(a -> Stream.single(a), b -> Stream.nil(), c -> Stream.nil()); + } + + public IO> traverseIO(final F> f) { + return e.either( + a -> f.f(a).map(Either3::left), + b -> IOFunctions.unit(middle(b)), + c -> IOFunctions.unit(right(c)) + ); + } + + public List> traverseList1(final F> f) { + return e.either( + a -> f.f(a).map(Either3::left), + b -> single(middle(b)), + c -> single(right(c)) + ); + } + + public Option> traverseOption(final F> f) { + return e.either( + a -> f.f(a).map(Either3::left), + b -> some(middle(b)), + c -> some(right(c)) + ); + + } + + public P1> traverseP1(final F> f) { + return e.either( + a -> f.f(a).map(Either3::left), + b -> p(middle(b)), + c -> p(right(c)) + ); + + } + + public Stream> traverseStream(final F> f) { + return e.either( + a -> f.f(a).map(Either3::left), + b -> Stream.single(middle(b)), + c -> Stream.single(right(c)) + ); + + } + } public static final class MiddleProjection { @@ -88,10 +195,150 @@ private MiddleProjection(final Either3 e) { this.e = e; } + public Either3 apply(final Either3, C> e) { + return e.middle().bind(this::map); + } + public Either3 bind(F> f) { return e.either(a -> left(a), b -> f.f(b), c -> right(c)); } + public Either3 either() { + return e; + } + + public boolean exists(final F f) { + return e.either(a -> false, b -> f.f(b), c -> false); + } + + public Option> filter(final F f) { + return e.either(a -> none(), b -> f.f(b) ? some(middle(b)) : none(), c -> none()); + } + + public boolean forall(final F f) { + return e.either(a -> true, b -> f.f(b), c -> true); + } + + public Unit foreach(final F f) { + return e.either(a -> unit(), b -> f.f(b), c -> unit()); + } + + public void foreachDoEffect(final Effect1 f) { + e.either(a -> unit(), b -> f.toF().f(b), c -> unit()); + } + + public Iterator iterator() { + return toList().iterator(); + } + + public Either3 map(final F f) { + return e.either(a -> left(a), b -> middle(f.f(b)), c -> right(c)); + } + + public B orValue(final B value) { + return orValue(() -> value); + } + + public B orValue(final F0 f) { + return e.either(a -> f.f(), b -> b, c -> f.f()); + } + + public Either3 sequence(final Either3 e) { + return bind(Function.constant(e)); + } + + public Array toArray() { + return e.either( + a -> Array.empty(), + b -> Array.single(b), + c -> Array.empty() + ); + } + + public Collection toCollection() { + return toList().toCollection(); + } + + public List toList() { + return e.either(a -> nil(), b -> single(b), c -> nil()); + } + + public Option toOption() { + return e.either(a -> none(), b -> some(b), c -> none()); + } + + public Stream toStream() { + return e.either(a -> Stream.nil(), b -> Stream.single(b), c -> Stream.nil()); + } + + public IO> traverseIO(final F> f) { + return e.either( + a -> IOFunctions.unit(left(a)), + b -> f.f(b).map(Either3::middle), + c -> IOFunctions.unit(right(c)) + ); + } + + public List> traverseList1(final F> f) { + return e.either( + a -> single(left(a)), + b -> f.f(b).map(Either3::middle), + c -> single(right(c)) + ); + } + + public Option> traverseOption(final F> f) { + return e.either( + a -> some(left(a)), + b -> f.f(b).map(Either3::middle), + c -> some(right(c)) + ); + + } + + public P1> traverseP1(final F> f) { + return e.either( + a -> p(left(a)), + b -> f.f(b).map(Either3::middle), + c -> p(right(c)) + ); + + } + + public Stream> traverseStream(final F> f) { + return e.either( + a -> Stream.single(left(a)), + b -> f.f(b).map(Either3::middle), + c -> Stream.single(right(c)) + ); + + } + + + } + + public final Either3 leftMap(F f) { + return left().map(f); + } + + public final F, Either3> leftMap_() { + return this::leftMap; + } + + public final Either3 middleMap(F f) { + return middle().map(f); + } + + public final F, Either3> middleMap_() { + return this::middleMap; + } + + public final Either3 rightMap(F f) { + return right().map(f); + } + + public final F, Either3> rightMap_() { + return this::rightMap; } public static final class RightProjection { @@ -104,12 +351,21 @@ private RightProjection(final Either3 e) { public Either3 bind(F> f) { return e.either(a -> left(a), b -> middle(b), c -> f.f(c)); } + + public Either3 map(final F f) { + return e.either(a -> left(a), b -> middle(b), c -> right(f.f(c))); + } + } public static Either3 left(A a) { return new Left<>(a); } + public static F> left_() { + return Either3::left; + } + public static Either3 middle(B b) { return new Middle<>(b); } @@ -140,6 +396,22 @@ public Either3 map3(F fl, F fm, F fr) { public abstract D either(F fa, F fb, F fc); + public static F, D> either_(F fa, F fb, F fc) { + return e -> e.either(fa, fb, fc); + } + + public static Either3 joinLeft(final Either3, B, C> e) { + return e.left().bind(identity()); + } + + public static Either3 joinMiddle(final Either3, C> e) { + return e.middle().bind(identity()); + } + + public static Either3 joinRight(final Either3> e) { + return e.right().bind(identity()); + } + public Either3 moveLeft() { return either(a -> right(a), b -> left(b), c -> middle(c)); } From eedacf0010d8db853fd18c5ba59384d7740f9a9c Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Wed, 7 Apr 2021 21:53:53 +1000 Subject: [PATCH 138/173] Implemented the right projection of Either3. --- core/src/main/java/fj/data/Either3.java | 111 ++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/core/src/main/java/fj/data/Either3.java b/core/src/main/java/fj/data/Either3.java index 0c7b7371..9b545f7e 100644 --- a/core/src/main/java/fj/data/Either3.java +++ b/core/src/main/java/fj/data/Either3.java @@ -347,15 +347,126 @@ public static final class RightProjection { private RightProjection(final Either3 e) { this.e = e; } + public Either3 apply(final Either3> e) { + return e.right().bind(this::map); + } public Either3 bind(F> f) { return e.either(a -> left(a), b -> middle(b), c -> f.f(c)); } + public Either3 either() { + return e; + } + + public boolean exists(final F f) { + return e.either(a -> false, b -> false, c -> f.f(c)); + } + + public Option> filter(final F f) { + return e.either(a -> none(), b -> none(), c -> f.f(c) ? some(right(c)) : none()); + } + + public boolean forall(final F f) { + return e.either(a -> true, b -> true, c -> f.f(c)); + } + + public Unit foreach(final F f) { + return e.either(a -> unit(), b -> unit(), c -> f.f(c)); + } + + public void foreachDoEffect(final Effect1 f) { + e.either(a -> unit(), b -> unit(), c -> f.toF().f(c)); + } + + public Iterator iterator() { + return toList().iterator(); + } + public Either3 map(final F f) { return e.either(a -> left(a), b -> middle(b), c -> right(f.f(c))); } + public C orValue(final C value) { + return orValue(() -> value); + } + + public C orValue(final F0 f) { + return e.either(a -> f.f(), b -> f.f(), c -> c); + } + + public Either3 sequence(final Either3 e) { + return bind(Function.constant(e)); + } + + public Array toArray() { + return e.either( + a -> Array.empty(), + b -> Array.empty(), + c -> Array.single(c) + ); + } + + public Collection toCollection() { + return toList().toCollection(); + } + + public List toList() { + return e.either(a -> nil(), b -> nil(), c -> single(c)); + } + + public Option toOption() { + return e.either(a -> none(), b -> none(), c -> some(c)); + } + + public Stream toStream() { + return e.either(a -> Stream.nil(), b -> Stream.nil(), c -> Stream.single(c)); + } + + public IO> traverseIO(final F> f) { + return e.either( + a -> IOFunctions.unit(left(a)), + b -> IOFunctions.unit(middle(b)), + c -> f.f(c).map(Either3::right) + ); + } + + public List> traverseList1(final F> f) { + return e.either( + a -> single(left(a)), + b -> single(middle(b)), + c -> f.f(c).map(Either3::right) + ); + } + + public Option> traverseOption(final F> f) { + return e.either( + a -> some(left(a)), + b -> some(middle(b)), + c -> f.f(c).map(Either3::right) + ); + + } + + public P1> traverseP1(final F> f) { + return e.either( + a -> p(left(a)), + b -> p(middle(b)), + c -> f.f(c).map(Either3::right) + ); + + } + + public Stream> traverseStream(final F> f) { + return e.either( + a -> Stream.single(left(a)), + b -> Stream.single(middle(b)), + c -> f.f(c).map(Either3::right) + ); + + } + + } public static Either3 left(A a) { From 546647277325db569386141681eefc99a889ec25 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 16 Apr 2021 22:39:20 +1000 Subject: [PATCH 139/173] Speed up Gradle tests by not generating test reports and running in parallel. --- build.gradle | 10 ++++++++++ gradle.properties | 3 +++ 2 files changed, 13 insertions(+) diff --git a/build.gradle b/build.gradle index 57c36c18..8e390f9c 100644 --- a/build.gradle +++ b/build.gradle @@ -70,6 +70,7 @@ allprojects { junitRuntime = "org.junit.vintage:junit-vintage-engine:5.5.2" displayCompilerWarnings = true + generateTestReports = false } repositories { @@ -112,6 +113,15 @@ subprojects { } } + tasks.withType(Test).configureEach { + maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 + if (!generateTestReports) { + reports.html.enabled = false + reports.junitXml.enabled = false + } + } + + } task coverage(type: org.gradle.testing.jacoco.tasks.JacocoReport) { diff --git a/gradle.properties b/gradle.properties index d4552a30..8f904ddb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,3 +3,6 @@ sonatypeUsername = incorrectUser sonatypePassword = incorrectPwd signingEnabled = false + +org.gradle.parallel = true + From 8cef774bf2fbd850bf0a5e13bb76d2fab8380ade Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 16 Apr 2021 22:44:17 +1000 Subject: [PATCH 140/173] Renamed show for Either3. --- core/src/main/java/fj/Show.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/fj/Show.java b/core/src/main/java/fj/Show.java index 62da5273..bfb2e151 100644 --- a/core/src/main/java/fj/Show.java +++ b/core/src/main/java/fj/Show.java @@ -256,7 +256,7 @@ public static Show> eitherShow(final Show sa, final Show< fromString("Right(").append(sb.f.f(e.right().value())).append(single(')'))); } - public static Show> eitherShow(final Show sa, final Show sb, final Show sc) { + public static Show> either3Show(final Show sa, final Show sb, final Show sc) { return show(e -> e.either( a -> fromString("Left(").append(sa.f.f(a)).append(single(')')), From 250aa82edd48edca4a315ee27f89cfc1bbb88e5b Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Sun, 18 Apr 2021 21:54:27 +1000 Subject: [PATCH 141/173] Make SafeIO inherit of F0. --- core/src/main/java/fj/data/SafeIO.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/fj/data/SafeIO.java b/core/src/main/java/fj/data/SafeIO.java index 11fdc31a..0643e886 100644 --- a/core/src/main/java/fj/data/SafeIO.java +++ b/core/src/main/java/fj/data/SafeIO.java @@ -1,13 +1,20 @@ package fj.data; +import fj.F0; + /** * Created by MarkPerry on 3/07/2014. */ @FunctionalInterface -public interface SafeIO extends IO { +public interface SafeIO extends IO, F0 { @Override A run(); + @Override + default A f() { + return run(); + } + } From 2cad57e8c28f3f61dc929adf1387b0a48a1c354b Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Sun, 18 Apr 2021 21:56:05 +1000 Subject: [PATCH 142/173] Added conversions from Effect0 to Try, TryEffect and F0. --- core/src/main/java/fj/function/Effect0.java | 29 ++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/fj/function/Effect0.java b/core/src/main/java/fj/function/Effect0.java index 57800eb0..5e499d2d 100644 --- a/core/src/main/java/fj/function/Effect0.java +++ b/core/src/main/java/fj/function/Effect0.java @@ -1,6 +1,8 @@ package fj.function; -import fj.F0; +import fj.*; + +import static fj.Unit.unit; /** * Created by mperry on 28/08/2014. @@ -9,4 +11,29 @@ public interface Effect0 { void f(); + default F0 toF0() { + return () -> { + f(); + return unit(); + }; + } + + default TryEffect0 toTryEffect0() { + return () -> f(); + } + + default Try0 toTry0() { + return () -> { + f(); + return unit(); + }; + } + + default P1 toP1() { + return P.lazy(() -> { + f(); + return unit(); + }); + } + } From 0f4f8548635e74c4fb9582887ad15c51acb1601f Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Sun, 18 Apr 2021 22:51:23 +1000 Subject: [PATCH 143/173] Implemented primary functions from F in Effect1. --- core/src/main/java/fj/function/Effect1.java | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/core/src/main/java/fj/function/Effect1.java b/core/src/main/java/fj/function/Effect1.java index 0cc35a39..64abed44 100644 --- a/core/src/main/java/fj/function/Effect1.java +++ b/core/src/main/java/fj/function/Effect1.java @@ -1,6 +1,8 @@ package fj.function; import fj.F; +import fj.P; +import fj.P1; import fj.Unit; import java.util.function.Consumer; @@ -18,6 +20,32 @@ default void accept(A a) { f(a); } + default F bind(final F> g) { + return a -> { + return g.f(toF().f(a)).f(a); + }; + } + + default void apply(A a) { + f(a); + } + + default Effect1 contramap(F f) { + return o(f); + } + + default F map(F f) { + return a -> f.f(toF().f(a)); + } + + default F andThen(final F f) { + return map(f); + } + + default Effect1 o(final F f) { + return c -> f(f.f(c)); + } + default F toF() { return a -> { f(a); @@ -25,4 +53,16 @@ default F toF() { }; } + static Effect1 fromF(F f) { + return a -> f.f(a); + } + + default F dimap(F f, F g) { + return c -> g.f(toF().f(f.f(c))); + } + + default P1 partial(final A a) { + return P.lazy(() -> toF().f(a)); + } + } From 53ab41a58021a17a109772918c9c02f08ed32f8f Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Mon, 19 Apr 2021 00:40:18 +1000 Subject: [PATCH 144/173] Added conversion functions between Effect, F, Try and TryEffect for low arities. --- core/src/main/java/fj/F0.java | 20 ++++++++ core/src/main/java/fj/data/SafeIO.java | 6 ++- core/src/main/java/fj/function/Effect1.java | 11 ++++ core/src/main/java/fj/function/Effect2.java | 12 +++++ core/src/main/java/fj/function/Try0.java | 37 ++++++++++++++ .../src/main/java/fj/function/TryEffect0.java | 50 +++++++++++++++++++ 6 files changed, 135 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/fj/F0.java b/core/src/main/java/fj/F0.java index c260992f..9354568c 100644 --- a/core/src/main/java/fj/F0.java +++ b/core/src/main/java/fj/F0.java @@ -1,5 +1,9 @@ package fj; +import fj.function.Effect0; +import fj.function.Try0; +import fj.function.TryEffect0; + import java.util.function.Supplier; /** @@ -14,4 +18,20 @@ default A get() { return f(); } + default Effect0 toEffect0() { + return () -> f(); + } + + default TryEffect0 toTryEffect0() { + return () -> f(); + } + + default Try0 toTry0() { + return () -> f(); + } + + default P1 toP1() { + return P.lazy(() -> f()); + } + } diff --git a/core/src/main/java/fj/data/SafeIO.java b/core/src/main/java/fj/data/SafeIO.java index 0643e886..eeef79f9 100644 --- a/core/src/main/java/fj/data/SafeIO.java +++ b/core/src/main/java/fj/data/SafeIO.java @@ -1,12 +1,16 @@ package fj.data; import fj.F0; +import fj.P; +import fj.P1; + +import java.io.IOException; /** * Created by MarkPerry on 3/07/2014. */ @FunctionalInterface -public interface SafeIO extends IO, F0 { +public interface SafeIO extends IO { @Override A run(); diff --git a/core/src/main/java/fj/function/Effect1.java b/core/src/main/java/fj/function/Effect1.java index 64abed44..aff85774 100644 --- a/core/src/main/java/fj/function/Effect1.java +++ b/core/src/main/java/fj/function/Effect1.java @@ -53,6 +53,15 @@ default F toF() { }; } + default TryEffect1 toTryEffect1() { + return a -> f(a); + } + + + default Try1 toTry1() { + return a -> toF().f(a); + } + static Effect1 fromF(F f) { return a -> f.f(a); } @@ -65,4 +74,6 @@ default P1 partial(final A a) { return P.lazy(() -> toF().f(a)); } + + } diff --git a/core/src/main/java/fj/function/Effect2.java b/core/src/main/java/fj/function/Effect2.java index 40d8abd8..e4d7701a 100644 --- a/core/src/main/java/fj/function/Effect2.java +++ b/core/src/main/java/fj/function/Effect2.java @@ -1,7 +1,12 @@ package fj.function; +import fj.F2; +import fj.Unit; + import java.util.function.BiConsumer; +import static fj.Unit.unit; + /** * Created by mperry on 28/08/2014. */ @@ -13,4 +18,11 @@ default void accept(A a, B b) { f(a, b); } + default F2 toF2() { + return (a, b) -> { + f(a, b); + return unit(); + }; + } + } diff --git a/core/src/main/java/fj/function/Try0.java b/core/src/main/java/fj/function/Try0.java index e218f0a7..9abfa6cd 100644 --- a/core/src/main/java/fj/function/Try0.java +++ b/core/src/main/java/fj/function/Try0.java @@ -1,5 +1,14 @@ package fj.function; +import fj.F0; +import fj.P; +import fj.P1; +import fj.data.Option; +import fj.data.Validation; + +import static fj.data.Validation.fail; +import static fj.data.Validation.success; + /** * A product of A which may throw an Exception. * @@ -13,4 +22,32 @@ public interface Try0 { A f() throws Z; + @SuppressWarnings("unchecked") + default F0> toF0() { + return () -> { + try { + return success(f()); + } catch (Exception e) { + return fail((Z) e); + } + }; + } + + default TryEffect0 toTryEffect0() { + return () -> f(); + } + + default Effect0 toEffect0() { + return () -> { + try { + f(); + } catch (Exception e) { + } + }; + } + + default P1> toP1() { + return P.lazy(() -> toF0().f()); + } + } diff --git a/core/src/main/java/fj/function/TryEffect0.java b/core/src/main/java/fj/function/TryEffect0.java index a6c9cbee..9a955c89 100644 --- a/core/src/main/java/fj/function/TryEffect0.java +++ b/core/src/main/java/fj/function/TryEffect0.java @@ -1,5 +1,15 @@ package fj.function; +import fj.F0; +import fj.P; +import fj.P1; +import fj.Unit; +import fj.data.Option; + +import static fj.Unit.unit; +import static fj.data.Option.none; +import static fj.data.Option.some; + /** * Created by mperry on 28/08/2014. */ @@ -7,4 +17,44 @@ public interface TryEffect0 { void f() throws Z; + @SuppressWarnings("unchecked") + default F0> toF0() { + return () -> { + try { + f(); + return none(); + } catch (Exception e) { + return some((Z) e); + } + }; + } + + @SuppressWarnings("unchecked") + default Try0 toTry0() { + return () -> { + try { + f(); + return unit(); + } catch (Exception e) { + throw ((Z) e); + } + }; + } + + default Effect0 toEffect0() { + return () -> { + try { + f(); + } catch (Exception e) { + } + }; + } + + default P1 toP1() { + return P.lazy(() -> { + toEffect0().f(); + return Unit.unit(); + }); + } + } From 19a70d92b43dfea29ae219a994230562d9375708 Mon Sep 17 00:00:00 2001 From: Chen Zhang <340355960@qq.com> Date: Tue, 17 Aug 2021 10:09:52 +0800 Subject: [PATCH 145/173] Improve Travis CI build Performance --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8e7c52c9..89680bd8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ jdk: - openjdk-ea matrix: + fast_finish: true include: - jdk: openjdk12 @@ -31,3 +32,8 @@ env: global: - secure: Bun+1FZ29Q3dR9gZ/5brxcSf+zcY5tWrsqOA4GUb5bYCMyORuXQB0FYXuhKR4wB1pFrk1a9EYwRwSu3GwRJVWb+UzF0CNOWF/QG5tGPx32IOXScwlL/KonI4Vhs7Oc0fF4Wdb7euNrT27BU61jbUugjJ642b3n0VBYFYDdquprU= - secure: QAxhjqLRa+WHKIzgIJPZ/rM5a5uzqG7E5rsC0YvB25cO712oYXmzsYPia/oSp0chXlYLYMfk2UnLeQCSx2e6ogXRRRa977Q+B33Nt0Hd9SGLtduv6DBrbA2ehLU12Ib4DWe5VhF5eueAunycYcllTvqA5h+pzTtEVbd68ZHncM4= + +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ From da4cd6264e102c96d2c78eba56fa57af12655d08 Mon Sep 17 00:00:00 2001 From: YunLemon <340355960@qq.com> Date: Wed, 18 Aug 2021 08:23:20 +0800 Subject: [PATCH 146/173] Update .travis.yml --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 89680bd8..a7465305 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,6 @@ matrix: script: - ./gradlew clean test - env: global: - secure: Bun+1FZ29Q3dR9gZ/5brxcSf+zcY5tWrsqOA4GUb5bYCMyORuXQB0FYXuhKR4wB1pFrk1a9EYwRwSu3GwRJVWb+UzF0CNOWF/QG5tGPx32IOXScwlL/KonI4Vhs7Oc0fF4Wdb7euNrT27BU61jbUugjJ642b3n0VBYFYDdquprU= From 20f6248c8a7ac7a8e0cedece07aca6b6de4306e1 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Wed, 9 Feb 2022 22:28:28 +1000 Subject: [PATCH 147/173] Set the default to signing disabled as per the 5.x branch --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 07c2b60c..d4552a30 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,4 @@ sonatypeUsername = incorrectUser sonatypePassword = incorrectPwd -signingEnabled = true +signingEnabled = false From 2387c3d3e8ff9c7899eda5e85365737477636edb Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Thu, 10 Feb 2022 20:34:40 +1000 Subject: [PATCH 148/173] Removed Java 9 options so the 5.x branch works with Java 8 --- build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.gradle b/build.gradle index 8e390f9c..81d2c1f1 100644 --- a/build.gradle +++ b/build.gradle @@ -107,7 +107,6 @@ subprojects { } tasks.withType(JavaCompile) { - options.compilerArgs.addAll(['--release', '8']) if (displayCompilerWarnings) { options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" } @@ -146,7 +145,6 @@ configure(subprojects.findAll { it.name != "props-core" }) { sourceCompatibility = "1.8" javadoc { - options.addBooleanOption('html5', true) } task javadocJar(type: Jar, dependsOn: "javadoc") { From baf69e9c359f8ee909b50fcddd0ed3b4eab438ce Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Thu, 10 Feb 2022 20:54:43 +1000 Subject: [PATCH 149/173] Template for version 5.0 release notes --- etc/release-notes/release-notes-5.0.adoc | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 etc/release-notes/release-notes-5.0.adoc diff --git a/etc/release-notes/release-notes-5.0.adoc b/etc/release-notes/release-notes-5.0.adoc new file mode 100644 index 00000000..89138acd --- /dev/null +++ b/etc/release-notes/release-notes-5.0.adoc @@ -0,0 +1,22 @@ + += Release 5.0 + +Released: TODO + +== Enhancements + +== Fixes + + +== Internal + + +== Breaking Changes + + +== Documentation + + + +== Contributors + From ed841cd150b552d11234baecbbd87adf5db135ed Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Thu, 10 Feb 2022 23:26:45 +1000 Subject: [PATCH 150/173] Added FJ 5.0 doc release notes --- etc/release-notes/release-notes-5.0.adoc | 37 ++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/etc/release-notes/release-notes-5.0.adoc b/etc/release-notes/release-notes-5.0.adoc index 89138acd..86cfc69e 100644 --- a/etc/release-notes/release-notes-5.0.adoc +++ b/etc/release-notes/release-notes-5.0.adoc @@ -4,19 +4,46 @@ Released: TODO == Enhancements +* The functions classes F, F2, F0, Effect1 and Effect2 extend the corresponding Java 8 function interface. Removed the corresponding classes F1W, F1Functions, F2W and F2Functions. Similarly for IO and IOW. +* Moved the function wrapper classes F1W and F2W into F and F2 as default functions. +* Added lifting a semigroup to an option monoid, using none as zero. +* Added Trampoline.suspend(F0>) +* Added sum, product and fromString to Longs. +* Added Bounded definition. +* Added toStream of Bounded in Enumerator. +* Added intersection monoid for sets. +* Added set intersection semigroup. +* Added FunctionalInterface annotations for interfaces F0, F, F2 to F8, IO and SafeIO. +* Added functions to IO. +* Added Either3. +* Updated IO and SafeIO inheritance. +* Added conversion functions for Effect, F, Try and TryEffect for low arities. == Fixes - +* Fixed BitSet properties test. == Internal - +* Upgraded to Gradle 6.8.3. +* Added Strategy, Validation, Integers, monoid, semigroup and monoid tests. +* Switch from the uptodate-gradle-plugin to gradle-versions-plugin. +* Speed up Gradle tests by running in parallel and not generating reports. == Breaking Changes - +* Removed Ord parameter from Monoid's setIntersectionMonoid function. +* Removed the classes F1W, F1Functions, F2W, F2Functions, F3W, F3Functions, F4W, F4Functions, F5W, F5Functions, F6W, F6Functions, F7W, F7Functions, F8W and F8Functions. +* Removed deprecated Monoid, Ord, P, P1, Semigroup, Array, HashMap, Java, List, Seq, Set, Stream, Gen, Rand and TreeMap functions. == Documentation - - +* Fixed the javadoc on Either's iif function. +* Fixed doc for union and intersection monoid for sets. +* Fixed semigroup docs. +* Fixed List.headOption. == Contributors +* Gabor Liptak +* Jean-Baptiste Giraudeau +* Soundharya Kamaraj +* Yaroslav Atroshenko +* Mark Perry +* Chen Zhang From f7a29a5be2df6dad22e4606a246e9b586d28f64b Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 11 Feb 2022 00:09:54 +1000 Subject: [PATCH 151/173] Removed build.number in the javadoc and created by Mark Perry --- build.gradle | 2 +- consume/src/test/java/fj/EmptyTest.java | 3 --- core/src/main/java/fj/Bottom.java | 2 -- core/src/main/java/fj/Class.java | 2 -- core/src/main/java/fj/Digit.java | 2 -- core/src/main/java/fj/Effect.java | 2 -- core/src/main/java/fj/Equal.java | 2 -- core/src/main/java/fj/F.java | 5 +---- core/src/main/java/fj/F0.java | 3 --- core/src/main/java/fj/F2.java | 3 --- core/src/main/java/fj/F3.java | 4 +--- core/src/main/java/fj/F4.java | 4 +--- core/src/main/java/fj/F5.java | 5 +---- core/src/main/java/fj/F6.java | 5 +---- core/src/main/java/fj/F7.java | 5 +---- core/src/main/java/fj/F8.java | 4 +--- core/src/main/java/fj/Function.java | 2 -- core/src/main/java/fj/Hash.java | 2 -- core/src/main/java/fj/LcgRng.java | 2 -- core/src/main/java/fj/Monoid.java | 2 -- core/src/main/java/fj/Ord.java | 2 -- core/src/main/java/fj/Ordering.java | 2 -- core/src/main/java/fj/P.java | 2 -- core/src/main/java/fj/P1.java | 10 +--------- core/src/main/java/fj/P2.java | 2 -- core/src/main/java/fj/P3.java | 2 -- core/src/main/java/fj/P4.java | 2 -- core/src/main/java/fj/P5.java | 2 -- core/src/main/java/fj/P6.java | 2 -- core/src/main/java/fj/P7.java | 2 -- core/src/main/java/fj/P8.java | 2 -- core/src/main/java/fj/Primitive.java | 2 -- core/src/main/java/fj/Rng.java | 3 --- core/src/main/java/fj/Semigroup.java | 2 -- core/src/main/java/fj/Show.java | 2 -- core/src/main/java/fj/Try.java | 3 --- core/src/main/java/fj/TryEffect.java | 3 --- core/src/main/java/fj/Unit.java | 2 -- core/src/main/java/fj/control/Trampoline.java | 2 +- core/src/main/java/fj/data/IO.java | 4 ---- core/src/main/java/fj/data/Iteratee.java | 3 --- core/src/main/java/fj/data/Java.java | 2 -- core/src/main/java/fj/data/Java8.java | 3 --- core/src/main/java/fj/data/List.java | 2 -- core/src/main/java/fj/data/NonEmptyList.java | 2 -- core/src/main/java/fj/data/Option.java | 2 -- core/src/main/java/fj/data/PriorityQueue.java | 2 -- core/src/main/java/fj/data/Reader.java | 1 - core/src/main/java/fj/data/SafeIO.java | 9 --------- core/src/main/java/fj/data/State.java | 3 --- core/src/main/java/fj/data/Stream.java | 2 -- core/src/main/java/fj/data/Tree.java | 2 -- core/src/main/java/fj/data/Validation.java | 2 -- core/src/main/java/fj/data/Writer.java | 3 --- core/src/main/java/fj/data/hamt/BitSet.java | 2 -- .../main/java/fj/data/hamt/HashArrayMappedTrie.java | 2 -- core/src/main/java/fj/data/hamt/Node.java | 2 -- core/src/main/java/fj/data/package-info.java | 2 -- core/src/main/java/fj/function/BigIntegers.java | 2 -- core/src/main/java/fj/function/Booleans.java | 2 -- core/src/main/java/fj/function/Doubles.java | 2 -- core/src/main/java/fj/function/Effect0.java | 3 --- core/src/main/java/fj/function/Effect1.java | 3 --- core/src/main/java/fj/function/Effect2.java | 3 --- core/src/main/java/fj/function/Effect3.java | 3 --- core/src/main/java/fj/function/Effect4.java | 3 --- core/src/main/java/fj/function/Effect5.java | 3 --- core/src/main/java/fj/function/Effect6.java | 3 --- core/src/main/java/fj/function/Effect7.java | 3 --- core/src/main/java/fj/function/Effect8.java | 3 --- core/src/main/java/fj/function/Integers.java | 2 -- core/src/main/java/fj/function/Longs.java | 2 -- core/src/main/java/fj/function/Strings.java | 2 -- core/src/main/java/fj/function/Try0.java | 1 - core/src/main/java/fj/function/Try1.java | 1 - core/src/main/java/fj/function/Try2.java | 1 - core/src/main/java/fj/function/Try3.java | 1 - core/src/main/java/fj/function/Try4.java | 1 - core/src/main/java/fj/function/Try5.java | 1 - core/src/main/java/fj/function/Try6.java | 1 - core/src/main/java/fj/function/Try7.java | 1 - core/src/main/java/fj/function/Try8.java | 1 - core/src/main/java/fj/function/TryEffect0.java | 3 --- core/src/main/java/fj/function/TryEffect1.java | 3 --- core/src/main/java/fj/function/TryEffect2.java | 3 --- core/src/main/java/fj/function/TryEffect3.java | 3 --- core/src/main/java/fj/function/TryEffect4.java | 3 --- core/src/main/java/fj/function/TryEffect5.java | 3 --- core/src/main/java/fj/function/TryEffect6.java | 3 --- core/src/main/java/fj/function/TryEffect7.java | 3 --- core/src/main/java/fj/function/TryEffect8.java | 3 --- core/src/main/java/fj/function/Visitor.java | 2 -- core/src/main/java/fj/function/package-info.java | 2 -- core/src/main/java/fj/package-info.java | 2 -- core/src/main/java/fj/parser/Parser.java | 2 -- core/src/main/java/fj/parser/Result.java | 2 -- core/src/main/java/fj/parser/package-info.java | 2 -- core/src/test/java/fj/P2Test.java | 3 --- core/src/test/java/fj/ShowTest.java | 3 --- core/src/test/java/fj/data/ArrayTest.java | 3 --- core/src/test/java/fj/data/BooleansTest.java | 3 --- core/src/test/java/fj/data/JavaTest.java | 3 --- core/src/test/java/fj/data/LazyStringTest.java | 3 --- core/src/test/java/fj/data/ListBufferTest.java | 3 --- core/src/test/java/fj/data/ListTest.java | 3 --- core/src/test/java/fj/data/List_Traverse_Tests.java | 3 --- core/src/test/java/fj/data/OptionTest.java | 3 --- core/src/test/java/fj/data/SeqTest.java | 3 --- core/src/test/java/fj/data/SetTest.java | 3 --- core/src/test/java/fj/data/StateTest.java | 3 --- core/src/test/java/fj/data/StreamTest.java | 3 --- core/src/test/java/fj/data/TreeMapTest.java | 3 --- core/src/test/java/fj/data/TreeTest.java | 6 +----- core/src/test/java/fj/data/UnitTest.java | 3 --- core/src/test/java/fj/function/TestEffect.java | 4 ---- demo/src/main/java/fj/demo/IODemo.java | 3 --- demo/src/main/java/fj/demo/StateDemo_Greeter.java | 3 --- .../main/java/fj/demo/StateDemo_VendingMachine.java | 3 --- demo/src/main/java/fj/demo/WriterDemo_Halver.java | 3 --- demo/src/main/java/fj/demo/optic/LensPerson.java | 3 --- demo/src/main/java/fj/demo/realworld/Chapter7.java | 2 -- demo/src/test/java/fj/EmptyTest.java | 3 --- java-core/src/main/java/fj/java/util/ListUtil.java | 3 --- java-core/src/test/java/fj/EmptyTest.java | 3 --- .../src/main/java/fj/data/dummy/DummyClass.java | 3 --- performance/src/test/java/fj/data/dummy/DummyTest.java | 3 --- props-core/src/test/java/fj/MemoisationTest.java | 3 --- props-core/src/test/java/fj/data/ReaderTest.java | 3 --- props-core/src/test/java/fj/data/TestRngState.java | 3 --- props-core/src/test/java/fj/data/WriterTest.java | 3 --- .../java/fj/data/fingertrees/FingerTreeProperties.java | 4 ---- .../test/java/fj/data/fingertrees/FingerTreeTest.java | 4 ---- .../src/test/java/fj/data/hamt/BitSetProperties.java | 4 ---- .../fj/data/properties/NonEmptyListProperties.java | 3 --- .../fj/data/properties/PriorityQueueProperties.java | 3 --- .../test/java/fj/data/properties/SetProperties.java | 3 --- .../java/fj/data/properties/TreeMapProperties.java | 3 --- .../java/fj/data/properties/ValidationProperties.java | 3 --- .../src/main/java/fj/data/test/PropertyAssert.java | 3 --- quickcheck/src/test/java/fj/data/test/TestNull.java | 3 --- quickcheck/src/test/java/fj/test/TestRand.java | 3 --- 141 files changed, 11 insertions(+), 374 deletions(-) diff --git a/build.gradle b/build.gradle index 81d2c1f1..1ceb4fb9 100644 --- a/build.gradle +++ b/build.gradle @@ -46,7 +46,7 @@ allprojects { snapshotAppendix = "-SNAPSHOT" fjVersion = fjBaseVersion + (isSnapshot ? snapshotAppendix : "") - fjConsumeVersion = "4.8.1" + fjConsumeVersion = "4.9" signModule = false diff --git a/consume/src/test/java/fj/EmptyTest.java b/consume/src/test/java/fj/EmptyTest.java index e112a97a..13675822 100644 --- a/consume/src/test/java/fj/EmptyTest.java +++ b/consume/src/test/java/fj/EmptyTest.java @@ -5,9 +5,6 @@ import org.junit.Assert; -/** - * Created by MarkPerry on 30/08/2015. - */ public class EmptyTest { @Ignore @Test diff --git a/core/src/main/java/fj/Bottom.java b/core/src/main/java/fj/Bottom.java index a70629f1..5cc7f4e4 100644 --- a/core/src/main/java/fj/Bottom.java +++ b/core/src/main/java/fj/Bottom.java @@ -2,8 +2,6 @@ /** * Represents the bottom _|_ value. - * - * @version %build.number% */ public final class Bottom { private Bottom() { diff --git a/core/src/main/java/fj/Class.java b/core/src/main/java/fj/Class.java index 56222de1..8bee1951 100644 --- a/core/src/main/java/fj/Class.java +++ b/core/src/main/java/fj/Class.java @@ -11,8 +11,6 @@ /** * A wrapper for a {@link java.lang.Class} that provides additional methods. - * - * @version %build.number% */ public final class Class { private final java.lang.Class c; diff --git a/core/src/main/java/fj/Digit.java b/core/src/main/java/fj/Digit.java index 7213e278..4fec7b0c 100644 --- a/core/src/main/java/fj/Digit.java +++ b/core/src/main/java/fj/Digit.java @@ -6,8 +6,6 @@ /** * The digits zero to nine. - * - * @version %build.number% */ public enum Digit { /** diff --git a/core/src/main/java/fj/Effect.java b/core/src/main/java/fj/Effect.java index 80680cac..2b5dcd6f 100644 --- a/core/src/main/java/fj/Effect.java +++ b/core/src/main/java/fj/Effect.java @@ -14,8 +14,6 @@ /** * Represents a side-effect. - * - * @version %build.number% */ public final class Effect { diff --git a/core/src/main/java/fj/Equal.java b/core/src/main/java/fj/Equal.java index 9179cc43..cba4a589 100644 --- a/core/src/main/java/fj/Equal.java +++ b/core/src/main/java/fj/Equal.java @@ -22,8 +22,6 @@ /** * Tests for equality between two objects. - * - * @version %build.number% */ public final class Equal { diff --git a/core/src/main/java/fj/F.java b/core/src/main/java/fj/F.java index 45cec4fe..1736f97b 100644 --- a/core/src/main/java/fj/F.java +++ b/core/src/main/java/fj/F.java @@ -16,10 +16,7 @@ import static fj.data.Zipper.fromStream; /** - * A transformation or function from A to B. This type can be represented - * using the Java 7 closure syntax. - * - * @version %build.number% + * A transformation or function from A to B. */ @FunctionalInterface public interface F extends Function { diff --git a/core/src/main/java/fj/F0.java b/core/src/main/java/fj/F0.java index 9354568c..88d60a9e 100644 --- a/core/src/main/java/fj/F0.java +++ b/core/src/main/java/fj/F0.java @@ -6,9 +6,6 @@ import java.util.function.Supplier; -/** - * Created by MarkPerry on 21/01/2015. - */ @FunctionalInterface public interface F0 extends Supplier { diff --git a/core/src/main/java/fj/F2.java b/core/src/main/java/fj/F2.java index 4f7c306d..8b1aac20 100644 --- a/core/src/main/java/fj/F2.java +++ b/core/src/main/java/fj/F2.java @@ -14,9 +14,6 @@ /** * A transformation function of arity-2 from A and B to C. - * This type can be represented using the Java 7 closure syntax. - * - * @version %build.number% */ @FunctionalInterface public interface F2 extends BiFunction { diff --git a/core/src/main/java/fj/F3.java b/core/src/main/java/fj/F3.java index b8c4dcf3..b7efeb3a 100644 --- a/core/src/main/java/fj/F3.java +++ b/core/src/main/java/fj/F3.java @@ -2,9 +2,7 @@ /** * A transformation function of arity-3 from A, B and C to - * D. This type can be represented using the Java 7 closure syntax. - * - * @version %build.number% + * D. */ @FunctionalInterface public interface F3 { diff --git a/core/src/main/java/fj/F4.java b/core/src/main/java/fj/F4.java index d63f03a7..c3c5f882 100644 --- a/core/src/main/java/fj/F4.java +++ b/core/src/main/java/fj/F4.java @@ -2,9 +2,7 @@ /** * A transformation function of arity-4 from A, B, C and - * D to E. This type can be represented using the Java 7 closure syntax. - * - * @version %build.number% + * D to E. */ @FunctionalInterface public interface F4 { diff --git a/core/src/main/java/fj/F5.java b/core/src/main/java/fj/F5.java index f63ae797..574afb15 100644 --- a/core/src/main/java/fj/F5.java +++ b/core/src/main/java/fj/F5.java @@ -2,10 +2,7 @@ /** * A transformation function of arity-5 from A, B, C, - * D and E to F$. This type can be represented using the Java - * 7 closure syntax. - * - * @version %build.number% + * D and E to F$. */ @FunctionalInterface public interface F5 { diff --git a/core/src/main/java/fj/F6.java b/core/src/main/java/fj/F6.java index 558c17fc..135e28fe 100644 --- a/core/src/main/java/fj/F6.java +++ b/core/src/main/java/fj/F6.java @@ -2,10 +2,7 @@ /** * A transformation function of arity-6 from A, B, C, - * D, E and F$ to G. This type can be - * represented using the Java 7 closure syntax. - * - * @version %build.number% + * D, E and F$ to G. */ @FunctionalInterface public interface F6 { diff --git a/core/src/main/java/fj/F7.java b/core/src/main/java/fj/F7.java index 05caefb4..a2542700 100644 --- a/core/src/main/java/fj/F7.java +++ b/core/src/main/java/fj/F7.java @@ -2,10 +2,7 @@ /** * A transformation function of arity-7 from A, B, C, - * D, E, F$ and G to H. This type - * can be represented using the Java 7 closure syntax. - * - * @version %build.number% + * D, E, F$ and G to H. */ @FunctionalInterface public interface F7 { diff --git a/core/src/main/java/fj/F8.java b/core/src/main/java/fj/F8.java index 5aa103f2..647fc14c 100644 --- a/core/src/main/java/fj/F8.java +++ b/core/src/main/java/fj/F8.java @@ -3,9 +3,7 @@ /** * A transformation function of arity-8 from A, B, C, * D, E, F$, G and H to - * I. This type can be represented using the Java 7 closure syntax. - * - * @version %build.number% + * I. */ @FunctionalInterface public interface F8 { diff --git a/core/src/main/java/fj/Function.java b/core/src/main/java/fj/Function.java index 995c179d..52819377 100644 --- a/core/src/main/java/fj/Function.java +++ b/core/src/main/java/fj/Function.java @@ -4,8 +4,6 @@ /** * Transformations on functions. - * - * @version %build.number% */ public final class Function { private Function() { diff --git a/core/src/main/java/fj/Hash.java b/core/src/main/java/fj/Hash.java index 703d96ee..241bcefb 100644 --- a/core/src/main/java/fj/Hash.java +++ b/core/src/main/java/fj/Hash.java @@ -17,8 +17,6 @@ /** * Produces a hash code for an object which should attempt uniqueness. - * - * @version %build.number% */ public final class Hash { private final F f; diff --git a/core/src/main/java/fj/LcgRng.java b/core/src/main/java/fj/LcgRng.java index 215b3572..cd918f5b 100644 --- a/core/src/main/java/fj/LcgRng.java +++ b/core/src/main/java/fj/LcgRng.java @@ -1,8 +1,6 @@ package fj; /** - * Created by MarkPerry on 7/07/2014. - * * https://en.wikipedia.org/wiki/Linear_congruential_generator */ public class LcgRng extends Rng { diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index bc09250d..bc05f33e 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -22,8 +22,6 @@ *

    1. Right Identity; forall x. sum(x, zero()) == x
    2. *
    3. Associativity; forall x y z. sum(sum(x, y), z) == sum(x, sum(y, z))
    4. * - * - * @version %build.number% */ public final class Monoid
      { diff --git a/core/src/main/java/fj/Ord.java b/core/src/main/java/fj/Ord.java index ae4d3fda..2bf90450 100644 --- a/core/src/main/java/fj/Ord.java +++ b/core/src/main/java/fj/Ord.java @@ -10,8 +10,6 @@ /** * Tests for ordering between two objects. - * - * @version %build.number% */ public final class Ord { diff --git a/core/src/main/java/fj/Ordering.java b/core/src/main/java/fj/Ordering.java index b22bcfcb..1828f69a 100644 --- a/core/src/main/java/fj/Ordering.java +++ b/core/src/main/java/fj/Ordering.java @@ -3,8 +3,6 @@ /** * The comparison of two instances of a type may have one of three orderings; less than, equal or * greater than. - * - * @version %build.number% */ public enum Ordering { /** diff --git a/core/src/main/java/fj/P.java b/core/src/main/java/fj/P.java index 587cdc9d..3e6efb74 100644 --- a/core/src/main/java/fj/P.java +++ b/core/src/main/java/fj/P.java @@ -4,8 +4,6 @@ /** * Functions across products. - * - * @version %build.number% */ public final class P { private P() { diff --git a/core/src/main/java/fj/P1.java b/core/src/main/java/fj/P1.java index 810e484d..5e575a4d 100644 --- a/core/src/main/java/fj/P1.java +++ b/core/src/main/java/fj/P1.java @@ -1,20 +1,12 @@ package fj; -import fj.data.Array; -import fj.data.Either; -import fj.data.List; -import fj.data.Option; -import fj.data.Stream; -import fj.data.Validation; +import fj.data.*; import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import static fj.P.p; -import static fj.Unit.unit; -//import fj.data.*; - public abstract class P1 implements F0 { diff --git a/core/src/main/java/fj/P2.java b/core/src/main/java/fj/P2.java index 841b356e..b4979f03 100644 --- a/core/src/main/java/fj/P2.java +++ b/core/src/main/java/fj/P2.java @@ -9,8 +9,6 @@ /** * A product-2. - * - * @version %build.number% */ public abstract class P2 { /** diff --git a/core/src/main/java/fj/P3.java b/core/src/main/java/fj/P3.java index 6e08a0ba..fff39ed8 100644 --- a/core/src/main/java/fj/P3.java +++ b/core/src/main/java/fj/P3.java @@ -4,8 +4,6 @@ /** * A product-3. - * - * @version %build.number% */ public abstract class P3 { /** diff --git a/core/src/main/java/fj/P4.java b/core/src/main/java/fj/P4.java index 4fa2ef8c..d2dca64b 100644 --- a/core/src/main/java/fj/P4.java +++ b/core/src/main/java/fj/P4.java @@ -4,8 +4,6 @@ /** * A product-4. - * - * @version %build.number% */ public abstract class P4 { /** diff --git a/core/src/main/java/fj/P5.java b/core/src/main/java/fj/P5.java index fabc4227..07138cb3 100644 --- a/core/src/main/java/fj/P5.java +++ b/core/src/main/java/fj/P5.java @@ -4,8 +4,6 @@ /** * A product-5. - * - * @version %build.number% */ public abstract class P5 { /** diff --git a/core/src/main/java/fj/P6.java b/core/src/main/java/fj/P6.java index 670622c7..295b5427 100644 --- a/core/src/main/java/fj/P6.java +++ b/core/src/main/java/fj/P6.java @@ -4,8 +4,6 @@ /** * A product-6. - * - * @version %build.number% */ @SuppressWarnings("UnnecessaryFullyQualifiedName") public abstract class P6 { diff --git a/core/src/main/java/fj/P7.java b/core/src/main/java/fj/P7.java index 6c9ac804..17161f5d 100644 --- a/core/src/main/java/fj/P7.java +++ b/core/src/main/java/fj/P7.java @@ -4,8 +4,6 @@ /** * A product-7. - * - * @version %build.number% */ @SuppressWarnings("UnnecessaryFullyQualifiedName") public abstract class P7 { diff --git a/core/src/main/java/fj/P8.java b/core/src/main/java/fj/P8.java index f3902c5e..571d6a15 100644 --- a/core/src/main/java/fj/P8.java +++ b/core/src/main/java/fj/P8.java @@ -4,8 +4,6 @@ /** * A product-8. - * - * @version %build.number% */ @SuppressWarnings("UnnecessaryFullyQualifiedName") public abstract class P8 { diff --git a/core/src/main/java/fj/Primitive.java b/core/src/main/java/fj/Primitive.java index 2007c03c..6ded797e 100644 --- a/core/src/main/java/fj/Primitive.java +++ b/core/src/main/java/fj/Primitive.java @@ -2,8 +2,6 @@ /** * Functions that convert between Java primitive types. - * - * @version %build.number% */ public final class Primitive { private Primitive() { diff --git a/core/src/main/java/fj/Rng.java b/core/src/main/java/fj/Rng.java index 20b3358f..46c31b27 100644 --- a/core/src/main/java/fj/Rng.java +++ b/core/src/main/java/fj/Rng.java @@ -1,8 +1,5 @@ package fj; -/** - * Created by MarkPerry on 7/07/2014. - */ public abstract class Rng { public abstract P2 nextInt(); diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index 1d767385..cd31e1e3 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -23,8 +23,6 @@ *
        *
      • Associativity; forall x. forall y. forall z. sum(sum(x, y), z) == sum(x, sum(y, z))
      • *
      - * - * @version %build.number% */ public final class Semigroup
      { diff --git a/core/src/main/java/fj/Show.java b/core/src/main/java/fj/Show.java index bfb2e151..8a21a281 100644 --- a/core/src/main/java/fj/Show.java +++ b/core/src/main/java/fj/Show.java @@ -27,8 +27,6 @@ /** * Renders an object for display. - * - * @version %build.number% */ public final class Show { private final F> f; diff --git a/core/src/main/java/fj/Try.java b/core/src/main/java/fj/Try.java index d8a30130..8418a621 100644 --- a/core/src/main/java/fj/Try.java +++ b/core/src/main/java/fj/Try.java @@ -10,9 +10,6 @@ import static fj.data.Validation.fail; import static fj.data.Validation.success; -/** - * Created by mperry on 24/07/2014. - */ public final class Try { private Try() { diff --git a/core/src/main/java/fj/TryEffect.java b/core/src/main/java/fj/TryEffect.java index cd1f75ab..a240e638 100644 --- a/core/src/main/java/fj/TryEffect.java +++ b/core/src/main/java/fj/TryEffect.java @@ -3,9 +3,6 @@ import fj.data.Validation; import fj.function.*; -/** - * Created by mperry on 29/08/2014. - */ public final class TryEffect { private TryEffect(){} diff --git a/core/src/main/java/fj/Unit.java b/core/src/main/java/fj/Unit.java index c7471f21..76463ec1 100644 --- a/core/src/main/java/fj/Unit.java +++ b/core/src/main/java/fj/Unit.java @@ -2,8 +2,6 @@ /** * The unit type which has only one value. - * - * @version %build.number% */ public final class Unit { private static final Unit u = new Unit(); diff --git a/core/src/main/java/fj/control/Trampoline.java b/core/src/main/java/fj/control/Trampoline.java index e3b58e1c..5c787f04 100644 --- a/core/src/main/java/fj/control/Trampoline.java +++ b/core/src/main/java/fj/control/Trampoline.java @@ -9,7 +9,7 @@ /** * A Trampoline is a potentially branching computation that can be stepped through and executed in constant stack. - * It represent suspendable coroutines with subroutine calls, reified as a data structure. + * It represents suspendable coroutines with subroutine calls, reified as a data structure. */ public abstract class Trampoline { diff --git a/core/src/main/java/fj/data/IO.java b/core/src/main/java/fj/data/IO.java index e2a94a38..69434728 100644 --- a/core/src/main/java/fj/data/IO.java +++ b/core/src/main/java/fj/data/IO.java @@ -1,9 +1,5 @@ package fj.data; -/** - * Created by MarkPerry on 19/06/2014. - */ - import fj.F; import fj.Unit; import fj.function.Try0; diff --git a/core/src/main/java/fj/data/Iteratee.java b/core/src/main/java/fj/data/Iteratee.java index be68ed02..e6177068 100644 --- a/core/src/main/java/fj/data/Iteratee.java +++ b/core/src/main/java/fj/data/Iteratee.java @@ -7,9 +7,6 @@ import fj.P2; import fj.Unit; -/** - * - */ public final class Iteratee { /** The input to an iteratee. */ diff --git a/core/src/main/java/fj/data/Java.java b/core/src/main/java/fj/data/Java.java index 8691c09d..b3cb7e7f 100644 --- a/core/src/main/java/fj/data/Java.java +++ b/core/src/main/java/fj/data/Java.java @@ -35,8 +35,6 @@ /** * Functions that convert between types from the core Java API. - * - * @version %build.number% */ public final class Java { private Java() { diff --git a/core/src/main/java/fj/data/Java8.java b/core/src/main/java/fj/data/Java8.java index 46c5ca2a..184c01c5 100644 --- a/core/src/main/java/fj/data/Java8.java +++ b/core/src/main/java/fj/data/Java8.java @@ -19,9 +19,6 @@ import java.util.function.Supplier; import java.util.stream.StreamSupport; -/** - * Created by mperry on 3/06/2014. - */ public final class Java8 { private Java8() { diff --git a/core/src/main/java/fj/data/List.java b/core/src/main/java/fj/data/List.java index 9259900f..721ee9f1 100644 --- a/core/src/main/java/fj/data/List.java +++ b/core/src/main/java/fj/data/List.java @@ -28,8 +28,6 @@ /** * Provides an in-memory, immutable, singly linked list. - * - * @version %build.number% */ public abstract class List implements Iterable { private List() { diff --git a/core/src/main/java/fj/data/NonEmptyList.java b/core/src/main/java/fj/data/NonEmptyList.java index 34c8b957..96bf444a 100644 --- a/core/src/main/java/fj/data/NonEmptyList.java +++ b/core/src/main/java/fj/data/NonEmptyList.java @@ -14,8 +14,6 @@ /** * Provides an in-memory, immutable, singly linked list with total head and tail. - * - * @version %build.number% */ public final class NonEmptyList implements Iterable { /** diff --git a/core/src/main/java/fj/data/Option.java b/core/src/main/java/fj/data/Option.java index b6876c63..9831c48c 100644 --- a/core/src/main/java/fj/data/Option.java +++ b/core/src/main/java/fj/data/Option.java @@ -23,8 +23,6 @@ /** * An optional value that may be none (no value) or some (a value). This type is a replacement for * the use of null with better type checks. - * - * @version %build.number% */ public abstract class Option implements Iterable { private Option() { diff --git a/core/src/main/java/fj/data/PriorityQueue.java b/core/src/main/java/fj/data/PriorityQueue.java index 21f6ed6f..f685c36d 100644 --- a/core/src/main/java/fj/data/PriorityQueue.java +++ b/core/src/main/java/fj/data/PriorityQueue.java @@ -20,8 +20,6 @@ * annotated with type K, are combined using a monoid of K and both the * key and value are stored in the leaf. Priorities of the same value * are returned FIFO (first in, first out). - * - * Created by MarkPerry on 31 May 16. */ public final class PriorityQueue { diff --git a/core/src/main/java/fj/data/Reader.java b/core/src/main/java/fj/data/Reader.java index 9374e300..49010e31 100644 --- a/core/src/main/java/fj/data/Reader.java +++ b/core/src/main/java/fj/data/Reader.java @@ -4,7 +4,6 @@ /** * The Reader monad (also called the function monad, so equivalent to the idea of F). - * Created by MarkPerry on 7/07/2014. */ public class Reader { diff --git a/core/src/main/java/fj/data/SafeIO.java b/core/src/main/java/fj/data/SafeIO.java index eeef79f9..bcc16bf6 100644 --- a/core/src/main/java/fj/data/SafeIO.java +++ b/core/src/main/java/fj/data/SafeIO.java @@ -1,14 +1,5 @@ package fj.data; -import fj.F0; -import fj.P; -import fj.P1; - -import java.io.IOException; - -/** - * Created by MarkPerry on 3/07/2014. - */ @FunctionalInterface public interface SafeIO extends IO { diff --git a/core/src/main/java/fj/data/State.java b/core/src/main/java/fj/data/State.java index f0156b36..c5b638a3 100644 --- a/core/src/main/java/fj/data/State.java +++ b/core/src/main/java/fj/data/State.java @@ -9,9 +9,6 @@ import static fj.control.Trampoline.suspend; import static fj.data.List.cons; -/** - * Created by MarkPerry on 7/07/2014. - */ public final class State { public static State unit(F> runF) { diff --git a/core/src/main/java/fj/data/Stream.java b/core/src/main/java/fj/data/Stream.java index bcf87dcf..8afa36ee 100644 --- a/core/src/main/java/fj/data/Stream.java +++ b/core/src/main/java/fj/data/Stream.java @@ -40,8 +40,6 @@ /** * A lazy (not yet evaluated), immutable, singly linked list. - * - * @version %build.number% */ public abstract class Stream implements Iterable { private Stream() { diff --git a/core/src/main/java/fj/data/Tree.java b/core/src/main/java/fj/data/Tree.java index f430d6d2..3b12d506 100644 --- a/core/src/main/java/fj/data/Tree.java +++ b/core/src/main/java/fj/data/Tree.java @@ -10,8 +10,6 @@ /** * Provides a lazy, immutable, non-empty, multi-way tree (a rose tree). - * - * @version %build.number% */ public final class Tree implements Iterable { /** diff --git a/core/src/main/java/fj/data/Validation.java b/core/src/main/java/fj/data/Validation.java index 60d8d2b2..5c1985a4 100644 --- a/core/src/main/java/fj/data/Validation.java +++ b/core/src/main/java/fj/data/Validation.java @@ -17,8 +17,6 @@ * Isomorphic to {@link Either} but has renamed functions and represents failure on the left and success on the right. * This type also has accumulating functions that accept a {@link Semigroup} for binding computation while keeping error * values - * - * @version %build.number% */ public class Validation implements Iterable { private final Either e; diff --git a/core/src/main/java/fj/data/Writer.java b/core/src/main/java/fj/data/Writer.java index b6b4579b..ba2cded5 100644 --- a/core/src/main/java/fj/data/Writer.java +++ b/core/src/main/java/fj/data/Writer.java @@ -2,9 +2,6 @@ import fj.*; -/** - * Created by MarkPerry on 7/07/2014. - */ public final class Writer { private final A val; diff --git a/core/src/main/java/fj/data/hamt/BitSet.java b/core/src/main/java/fj/data/hamt/BitSet.java index d164642d..9efbab34 100644 --- a/core/src/main/java/fj/data/hamt/BitSet.java +++ b/core/src/main/java/fj/data/hamt/BitSet.java @@ -13,8 +13,6 @@ * For example, the BitSet("1011") represents the decimal number 11 and has * indices [3, 0] inclusive where the bit with the lowest value has the lowest * index and is the rightmost bit. - * - * Created by maperr on 31/05/2016. */ public final class BitSet { diff --git a/core/src/main/java/fj/data/hamt/HashArrayMappedTrie.java b/core/src/main/java/fj/data/hamt/HashArrayMappedTrie.java index f13ae90f..371f5435 100644 --- a/core/src/main/java/fj/data/hamt/HashArrayMappedTrie.java +++ b/core/src/main/java/fj/data/hamt/HashArrayMappedTrie.java @@ -22,8 +22,6 @@ * mapped trie. It is a refined version of the more general notion of * a hash tree. * - * @author Mark Perry - * * Based on "Ideal Hash Trees" by Phil Bagwell, available from * http://lampwww.epfl.ch/papers/idealhashtrees.pdf */ diff --git a/core/src/main/java/fj/data/hamt/Node.java b/core/src/main/java/fj/data/hamt/Node.java index f8a1ff40..af453925 100644 --- a/core/src/main/java/fj/data/hamt/Node.java +++ b/core/src/main/java/fj/data/hamt/Node.java @@ -10,8 +10,6 @@ /** * A Hash Array Mapped Trie node that is either a key-value pair or a * Hash Array Mapped Trie. - * - * Created by maperr on 31/05/2016. */ public final class Node { diff --git a/core/src/main/java/fj/data/package-info.java b/core/src/main/java/fj/data/package-info.java index 911ef492..ae9577a7 100644 --- a/core/src/main/java/fj/data/package-info.java +++ b/core/src/main/java/fj/data/package-info.java @@ -1,6 +1,4 @@ /** * Common algebraic data types. - * - * @version %build.number% */ package fj.data; diff --git a/core/src/main/java/fj/function/BigIntegers.java b/core/src/main/java/fj/function/BigIntegers.java index 30d07a10..ac6584fc 100644 --- a/core/src/main/java/fj/function/BigIntegers.java +++ b/core/src/main/java/fj/function/BigIntegers.java @@ -10,8 +10,6 @@ /** * Curried functions over Integers. - * - * @version %build.number% */ public final class BigIntegers { private BigIntegers() { diff --git a/core/src/main/java/fj/function/Booleans.java b/core/src/main/java/fj/function/Booleans.java index 6211e870..bef1b347 100644 --- a/core/src/main/java/fj/function/Booleans.java +++ b/core/src/main/java/fj/function/Booleans.java @@ -15,8 +15,6 @@ /** * Curried logical functions. - * - * @version %build.number% */ public final class Booleans { private Booleans() { diff --git a/core/src/main/java/fj/function/Doubles.java b/core/src/main/java/fj/function/Doubles.java index 6fe1b033..aefcbfdc 100644 --- a/core/src/main/java/fj/function/Doubles.java +++ b/core/src/main/java/fj/function/Doubles.java @@ -11,8 +11,6 @@ /** * Curried functions over Doubles. - * - * @version %build.number% */ public final class Doubles { private Doubles() { diff --git a/core/src/main/java/fj/function/Effect0.java b/core/src/main/java/fj/function/Effect0.java index 5e499d2d..7644d394 100644 --- a/core/src/main/java/fj/function/Effect0.java +++ b/core/src/main/java/fj/function/Effect0.java @@ -4,9 +4,6 @@ import static fj.Unit.unit; -/** - * Created by mperry on 28/08/2014. - */ public interface Effect0 { void f(); diff --git a/core/src/main/java/fj/function/Effect1.java b/core/src/main/java/fj/function/Effect1.java index aff85774..1f3be909 100644 --- a/core/src/main/java/fj/function/Effect1.java +++ b/core/src/main/java/fj/function/Effect1.java @@ -9,9 +9,6 @@ import static fj.Unit.unit; -/** - * Created by mperry on 28/08/2014. - */ public interface Effect1 extends Consumer { void f(A a); diff --git a/core/src/main/java/fj/function/Effect2.java b/core/src/main/java/fj/function/Effect2.java index e4d7701a..b9a5ad5a 100644 --- a/core/src/main/java/fj/function/Effect2.java +++ b/core/src/main/java/fj/function/Effect2.java @@ -7,9 +7,6 @@ import static fj.Unit.unit; -/** - * Created by mperry on 28/08/2014. - */ public interface Effect2 extends BiConsumer { void f(A a, B b); diff --git a/core/src/main/java/fj/function/Effect3.java b/core/src/main/java/fj/function/Effect3.java index 0609584b..675d2301 100644 --- a/core/src/main/java/fj/function/Effect3.java +++ b/core/src/main/java/fj/function/Effect3.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface Effect3 { void f(A a, B b, C c); diff --git a/core/src/main/java/fj/function/Effect4.java b/core/src/main/java/fj/function/Effect4.java index b24a7624..fdd20af8 100644 --- a/core/src/main/java/fj/function/Effect4.java +++ b/core/src/main/java/fj/function/Effect4.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface Effect4 { void f(A a, B b, C c, D d); diff --git a/core/src/main/java/fj/function/Effect5.java b/core/src/main/java/fj/function/Effect5.java index 142be280..48634ac8 100644 --- a/core/src/main/java/fj/function/Effect5.java +++ b/core/src/main/java/fj/function/Effect5.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface Effect5 { void f(A a, B b, C c, D d, E e); diff --git a/core/src/main/java/fj/function/Effect6.java b/core/src/main/java/fj/function/Effect6.java index fe72314b..f81cdf25 100644 --- a/core/src/main/java/fj/function/Effect6.java +++ b/core/src/main/java/fj/function/Effect6.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface Effect6 { void f(A a, B b, C c, D d, E e, F f); diff --git a/core/src/main/java/fj/function/Effect7.java b/core/src/main/java/fj/function/Effect7.java index 944273af..4c77e4f4 100644 --- a/core/src/main/java/fj/function/Effect7.java +++ b/core/src/main/java/fj/function/Effect7.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface Effect7 { void f(A a, B b, C c, D d, E e, F f, G g); diff --git a/core/src/main/java/fj/function/Effect8.java b/core/src/main/java/fj/function/Effect8.java index a8cfd3bb..d8b970ec 100644 --- a/core/src/main/java/fj/function/Effect8.java +++ b/core/src/main/java/fj/function/Effect8.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface Effect8 { void f(A a, B b, C c, D d, E e, F f, G g, H h); diff --git a/core/src/main/java/fj/function/Integers.java b/core/src/main/java/fj/function/Integers.java index 4b1b14b5..7f8fd0e8 100644 --- a/core/src/main/java/fj/function/Integers.java +++ b/core/src/main/java/fj/function/Integers.java @@ -16,8 +16,6 @@ /** * Curried functions over Integers. - * - * @version %build.number% */ public final class Integers { private Integers() { diff --git a/core/src/main/java/fj/function/Longs.java b/core/src/main/java/fj/function/Longs.java index 0989e8c0..2762f3e2 100644 --- a/core/src/main/java/fj/function/Longs.java +++ b/core/src/main/java/fj/function/Longs.java @@ -15,8 +15,6 @@ /** * Curried functions over Longs. - * - * @version %build.number% */ public final class Longs { private Longs() { diff --git a/core/src/main/java/fj/function/Strings.java b/core/src/main/java/fj/function/Strings.java index dadbfd22..1c239e85 100644 --- a/core/src/main/java/fj/function/Strings.java +++ b/core/src/main/java/fj/function/Strings.java @@ -12,8 +12,6 @@ /** * Curried string functions. - * - * @version %build.number% */ public final class Strings { private Strings() { diff --git a/core/src/main/java/fj/function/Try0.java b/core/src/main/java/fj/function/Try0.java index 9abfa6cd..7cdacfc1 100644 --- a/core/src/main/java/fj/function/Try0.java +++ b/core/src/main/java/fj/function/Try0.java @@ -15,7 +15,6 @@ * Used to instantiate a lambda that may throw an Exception before converting to a P1. * * @see fj.Try#f(Try0) - * @version %build.number% */ public interface Try0 { diff --git a/core/src/main/java/fj/function/Try1.java b/core/src/main/java/fj/function/Try1.java index b626890b..53f5d048 100644 --- a/core/src/main/java/fj/function/Try1.java +++ b/core/src/main/java/fj/function/Try1.java @@ -6,7 +6,6 @@ * Used to instantiate a lambda that may throw an Exception before converting to an F. * * @see fj.Try#f(Try1) - * @version %build.number% */ public interface Try1 { diff --git a/core/src/main/java/fj/function/Try2.java b/core/src/main/java/fj/function/Try2.java index 94438ccb..24e6a099 100644 --- a/core/src/main/java/fj/function/Try2.java +++ b/core/src/main/java/fj/function/Try2.java @@ -6,7 +6,6 @@ * Used to instantiate a lambda that may throw an Exception before converting to an F2. * * @see fj.Try#f(Try2) - * @version %build.number% */ public interface Try2 { diff --git a/core/src/main/java/fj/function/Try3.java b/core/src/main/java/fj/function/Try3.java index 7464c013..7c0de0eb 100644 --- a/core/src/main/java/fj/function/Try3.java +++ b/core/src/main/java/fj/function/Try3.java @@ -6,7 +6,6 @@ * Used to instantiate a lambda that may throw an Exception before converting to an F3. * * @see fj.Try#f(Try3) - * @version %build.number% */ public interface Try3 { diff --git a/core/src/main/java/fj/function/Try4.java b/core/src/main/java/fj/function/Try4.java index a7d969ed..d2b79d2c 100644 --- a/core/src/main/java/fj/function/Try4.java +++ b/core/src/main/java/fj/function/Try4.java @@ -6,7 +6,6 @@ * Used to instantiate a lambda that may throw an Exception before converting to an F4. * * @see fj.Try#f(Try4) - * @version %build.number% */ public interface Try4 { diff --git a/core/src/main/java/fj/function/Try5.java b/core/src/main/java/fj/function/Try5.java index b9855fd7..cbc1a93d 100644 --- a/core/src/main/java/fj/function/Try5.java +++ b/core/src/main/java/fj/function/Try5.java @@ -6,7 +6,6 @@ * Used to instantiate a lambda that may throw an Exception before converting to an F5. * * @see fj.Try#f(Try5) - * @version %build.number% */ public interface Try5 { diff --git a/core/src/main/java/fj/function/Try6.java b/core/src/main/java/fj/function/Try6.java index b66775c6..39bffbcb 100644 --- a/core/src/main/java/fj/function/Try6.java +++ b/core/src/main/java/fj/function/Try6.java @@ -6,7 +6,6 @@ * Used to instantiate a lambda that may throw an Exception before converting to an F6. * * @see fj.Try#f(Try6) - * @version %build.number% */ public interface Try6 { diff --git a/core/src/main/java/fj/function/Try7.java b/core/src/main/java/fj/function/Try7.java index 3bc09cd9..ab56ffcd 100644 --- a/core/src/main/java/fj/function/Try7.java +++ b/core/src/main/java/fj/function/Try7.java @@ -7,7 +7,6 @@ * Used to instantiate a lambda that may throw an Exception before converting to an F7. * * @see fj.Try#f(Try7) - * @version %build.number% */ public interface Try7 { diff --git a/core/src/main/java/fj/function/Try8.java b/core/src/main/java/fj/function/Try8.java index 5b2f1b55..1486de3c 100644 --- a/core/src/main/java/fj/function/Try8.java +++ b/core/src/main/java/fj/function/Try8.java @@ -6,7 +6,6 @@ * Used to instantiate a lambda that may throw an Exception before converting to an F8. * * @see fj.Try#f(Try8) - * @version %build.number% */ public interface Try8 { diff --git a/core/src/main/java/fj/function/TryEffect0.java b/core/src/main/java/fj/function/TryEffect0.java index 9a955c89..1ccaa9af 100644 --- a/core/src/main/java/fj/function/TryEffect0.java +++ b/core/src/main/java/fj/function/TryEffect0.java @@ -10,9 +10,6 @@ import static fj.data.Option.none; import static fj.data.Option.some; -/** - * Created by mperry on 28/08/2014. - */ public interface TryEffect0 { void f() throws Z; diff --git a/core/src/main/java/fj/function/TryEffect1.java b/core/src/main/java/fj/function/TryEffect1.java index 40db423d..1ddaca08 100644 --- a/core/src/main/java/fj/function/TryEffect1.java +++ b/core/src/main/java/fj/function/TryEffect1.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface TryEffect1 { void f(A a) throws Z; diff --git a/core/src/main/java/fj/function/TryEffect2.java b/core/src/main/java/fj/function/TryEffect2.java index c9c13da9..c54f3fe7 100644 --- a/core/src/main/java/fj/function/TryEffect2.java +++ b/core/src/main/java/fj/function/TryEffect2.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface TryEffect2 { void f(A a, B b) throws Z; diff --git a/core/src/main/java/fj/function/TryEffect3.java b/core/src/main/java/fj/function/TryEffect3.java index c581221e..4c3f19d0 100644 --- a/core/src/main/java/fj/function/TryEffect3.java +++ b/core/src/main/java/fj/function/TryEffect3.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface TryEffect3 { void f(A a, B b, C c) throws Z; diff --git a/core/src/main/java/fj/function/TryEffect4.java b/core/src/main/java/fj/function/TryEffect4.java index f99900ca..8b04ed5f 100644 --- a/core/src/main/java/fj/function/TryEffect4.java +++ b/core/src/main/java/fj/function/TryEffect4.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface TryEffect4 { void f(A a, B b, C c, D d) throws Z; diff --git a/core/src/main/java/fj/function/TryEffect5.java b/core/src/main/java/fj/function/TryEffect5.java index d0445e3e..0bca874a 100644 --- a/core/src/main/java/fj/function/TryEffect5.java +++ b/core/src/main/java/fj/function/TryEffect5.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface TryEffect5 { void f(A a, B b, C c, D d, E e) throws Z; diff --git a/core/src/main/java/fj/function/TryEffect6.java b/core/src/main/java/fj/function/TryEffect6.java index b4d81a41..d8f1f468 100644 --- a/core/src/main/java/fj/function/TryEffect6.java +++ b/core/src/main/java/fj/function/TryEffect6.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface TryEffect6 { void f(A a, B b, C c, D d, E e, F f) throws Z; diff --git a/core/src/main/java/fj/function/TryEffect7.java b/core/src/main/java/fj/function/TryEffect7.java index 2e6004e9..133212dc 100644 --- a/core/src/main/java/fj/function/TryEffect7.java +++ b/core/src/main/java/fj/function/TryEffect7.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface TryEffect7 { void f(A a, B b, C c, D d, E e, F f, G g) throws Z; diff --git a/core/src/main/java/fj/function/TryEffect8.java b/core/src/main/java/fj/function/TryEffect8.java index 4424bf00..d9c8c384 100644 --- a/core/src/main/java/fj/function/TryEffect8.java +++ b/core/src/main/java/fj/function/TryEffect8.java @@ -1,8 +1,5 @@ package fj.function; -/** - * Created by mperry on 28/08/2014. - */ public interface TryEffect8 { void f(A a, B b, C c, D d, E e, F f, G g, H h) throws Z; diff --git a/core/src/main/java/fj/function/Visitor.java b/core/src/main/java/fj/function/Visitor.java index bdca3b5e..ed0e7b53 100644 --- a/core/src/main/java/fj/function/Visitor.java +++ b/core/src/main/java/fj/function/Visitor.java @@ -15,8 +15,6 @@ /** * The essence of the visitor design pattern expressed polymorphically. - * - * @version %build.number% */ public final class Visitor { private Visitor() { diff --git a/core/src/main/java/fj/function/package-info.java b/core/src/main/java/fj/function/package-info.java index 6698bd5d..851bb534 100644 --- a/core/src/main/java/fj/function/package-info.java +++ b/core/src/main/java/fj/function/package-info.java @@ -1,6 +1,4 @@ /** * A prelude of commonly used first-class functions - * - * @version %build.number% */ package fj.function; diff --git a/core/src/main/java/fj/package-info.java b/core/src/main/java/fj/package-info.java index abeffe0f..c457d1e9 100644 --- a/core/src/main/java/fj/package-info.java +++ b/core/src/main/java/fj/package-info.java @@ -1,6 +1,4 @@ /** * Types that set the premise for the existence of Functional Java. - * - * @version %build.number% */ package fj; diff --git a/core/src/main/java/fj/parser/Parser.java b/core/src/main/java/fj/parser/Parser.java index 3ea6dd15..b5753f23 100644 --- a/core/src/main/java/fj/parser/Parser.java +++ b/core/src/main/java/fj/parser/Parser.java @@ -14,8 +14,6 @@ /** * A parser is a function that takes some input (I) and produces either an error (E) or a parse result (A) and the * remainder of the input. - * - * @version %build.number% */ public final class Parser { private final F>> f; diff --git a/core/src/main/java/fj/parser/Result.java b/core/src/main/java/fj/parser/Result.java index b8acc6c4..843dd558 100644 --- a/core/src/main/java/fj/parser/Result.java +++ b/core/src/main/java/fj/parser/Result.java @@ -12,8 +12,6 @@ /** * A parse result made up of a value (A) and the remainder of the parse input (I). - * - * @version %build.number% */ public final class Result implements Iterable { private final I i; diff --git a/core/src/main/java/fj/parser/package-info.java b/core/src/main/java/fj/parser/package-info.java index 08140f0b..8f31c204 100644 --- a/core/src/main/java/fj/parser/package-info.java +++ b/core/src/main/java/fj/parser/package-info.java @@ -1,6 +1,4 @@ /** * Parser combinators. - * - * @version %build.number% */ package fj.parser; diff --git a/core/src/test/java/fj/P2Test.java b/core/src/test/java/fj/P2Test.java index dc25a3e0..d14b6199 100644 --- a/core/src/test/java/fj/P2Test.java +++ b/core/src/test/java/fj/P2Test.java @@ -3,9 +3,6 @@ import org.junit.Assert; import org.junit.Test; -/** - * Created by MarkPerry on 22/07/2014. - */ public class P2Test { @Test diff --git a/core/src/test/java/fj/ShowTest.java b/core/src/test/java/fj/ShowTest.java index c516c0df..3d6d8354 100644 --- a/core/src/test/java/fj/ShowTest.java +++ b/core/src/test/java/fj/ShowTest.java @@ -6,9 +6,6 @@ import static fj.data.Array.array; import static org.junit.Assert.assertTrue; -/** - * Created by MarkPerry on 4/06/2015. - */ public class ShowTest { @Test public void arrayShow() { diff --git a/core/src/test/java/fj/data/ArrayTest.java b/core/src/test/java/fj/data/ArrayTest.java index 883fc835..5713e5c3 100644 --- a/core/src/test/java/fj/data/ArrayTest.java +++ b/core/src/test/java/fj/data/ArrayTest.java @@ -7,9 +7,6 @@ import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.Assert.assertThat; -/** - * Created by MarkPerry on 14 Feb 16. - */ public class ArrayTest { @Test diff --git a/core/src/test/java/fj/data/BooleansTest.java b/core/src/test/java/fj/data/BooleansTest.java index b0d3f969..84f7676f 100644 --- a/core/src/test/java/fj/data/BooleansTest.java +++ b/core/src/test/java/fj/data/BooleansTest.java @@ -10,9 +10,6 @@ import static fj.function.Booleans.isnot; import static org.hamcrest.core.Is.is; -/** - * Created by amar on 28/01/15. - */ public class BooleansTest { @Test diff --git a/core/src/test/java/fj/data/JavaTest.java b/core/src/test/java/fj/data/JavaTest.java index 2a9b6bfd..b8d99510 100644 --- a/core/src/test/java/fj/data/JavaTest.java +++ b/core/src/test/java/fj/data/JavaTest.java @@ -9,9 +9,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -/** - * Created by MarkPerry on 14/07/2014. - */ public class JavaTest { @Test diff --git a/core/src/test/java/fj/data/LazyStringTest.java b/core/src/test/java/fj/data/LazyStringTest.java index 4f6325af..1adef2bd 100644 --- a/core/src/test/java/fj/data/LazyStringTest.java +++ b/core/src/test/java/fj/data/LazyStringTest.java @@ -6,9 +6,6 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -/** - * Created by MarkPerry on 11/06/2015. - */ public class LazyStringTest { @Test diff --git a/core/src/test/java/fj/data/ListBufferTest.java b/core/src/test/java/fj/data/ListBufferTest.java index 1dfbbc39..f2ed8286 100644 --- a/core/src/test/java/fj/data/ListBufferTest.java +++ b/core/src/test/java/fj/data/ListBufferTest.java @@ -5,9 +5,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; -/** - * Created by MarkPerry on 17/08/2015. - */ public class ListBufferTest { @Test diff --git a/core/src/test/java/fj/data/ListTest.java b/core/src/test/java/fj/data/ListTest.java index ca7d0f22..9f09de0f 100644 --- a/core/src/test/java/fj/data/ListTest.java +++ b/core/src/test/java/fj/data/ListTest.java @@ -32,9 +32,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.*; -/** - * Created by MarkPerry on 16/01/2015. - */ public class ListTest { @Test diff --git a/core/src/test/java/fj/data/List_Traverse_Tests.java b/core/src/test/java/fj/data/List_Traverse_Tests.java index 3d964f45..5b7c0d19 100644 --- a/core/src/test/java/fj/data/List_Traverse_Tests.java +++ b/core/src/test/java/fj/data/List_Traverse_Tests.java @@ -9,9 +9,6 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -/** - * Created by amar on 28/12/14. - */ public class List_Traverse_Tests { @Test diff --git a/core/src/test/java/fj/data/OptionTest.java b/core/src/test/java/fj/data/OptionTest.java index 5a7bbdbf..e03d7aae 100644 --- a/core/src/test/java/fj/data/OptionTest.java +++ b/core/src/test/java/fj/data/OptionTest.java @@ -31,9 +31,6 @@ import static fj.data.Validation.*; import static org.junit.Assert.*; -/** - * Created by MarkPerry on 15/01/2015. - */ public final class OptionTest { @Test diff --git a/core/src/test/java/fj/data/SeqTest.java b/core/src/test/java/fj/data/SeqTest.java index 0b62db00..8a78ab31 100644 --- a/core/src/test/java/fj/data/SeqTest.java +++ b/core/src/test/java/fj/data/SeqTest.java @@ -33,9 +33,6 @@ import static org.hamcrest.core.Is.is; import static org.junit.Assert.*; -/** - * Created by MarkPerry on 16/01/2015. - */ public class SeqTest { @Test diff --git a/core/src/test/java/fj/data/SetTest.java b/core/src/test/java/fj/data/SetTest.java index cdf2519c..ae65b687 100644 --- a/core/src/test/java/fj/data/SetTest.java +++ b/core/src/test/java/fj/data/SetTest.java @@ -8,9 +8,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; -/** - * Created by MarkPerry on 18/08/2015. - */ public class SetTest { @Test diff --git a/core/src/test/java/fj/data/StateTest.java b/core/src/test/java/fj/data/StateTest.java index 37b21030..479efc38 100644 --- a/core/src/test/java/fj/data/StateTest.java +++ b/core/src/test/java/fj/data/StateTest.java @@ -5,9 +5,6 @@ import static fj.P.p; import static org.junit.Assert.assertEquals; -/** - * Created by MarkPerry on 18/12/2014. - */ public class StateTest { @Test diff --git a/core/src/test/java/fj/data/StreamTest.java b/core/src/test/java/fj/data/StreamTest.java index 313678aa..f320f27a 100644 --- a/core/src/test/java/fj/data/StreamTest.java +++ b/core/src/test/java/fj/data/StreamTest.java @@ -38,9 +38,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; -/** - * Created by Zheka Kozlov on 27.05.2015. - */ public class StreamTest { @Test diff --git a/core/src/test/java/fj/data/TreeMapTest.java b/core/src/test/java/fj/data/TreeMapTest.java index 96cb95b2..77352989 100644 --- a/core/src/test/java/fj/data/TreeMapTest.java +++ b/core/src/test/java/fj/data/TreeMapTest.java @@ -19,9 +19,6 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -/** - * Created by MarkPerry on 11/01/2015. - */ public class TreeMapTest { @Test diff --git a/core/src/test/java/fj/data/TreeTest.java b/core/src/test/java/fj/data/TreeTest.java index 811826fb..fd43c852 100644 --- a/core/src/test/java/fj/data/TreeTest.java +++ b/core/src/test/java/fj/data/TreeTest.java @@ -1,15 +1,11 @@ package fj.data; -import org.junit.Assert; import org.junit.Test; import static fj.data.Tree.leaf; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.*; +import static org.junit.Assert.assertThat; -/** - * Created by MarkPerry on 29/08/2015. - */ public class TreeTest { @Test diff --git a/core/src/test/java/fj/data/UnitTest.java b/core/src/test/java/fj/data/UnitTest.java index eef15887..1fab0a7b 100644 --- a/core/src/test/java/fj/data/UnitTest.java +++ b/core/src/test/java/fj/data/UnitTest.java @@ -4,9 +4,6 @@ import org.junit.Assert; import org.junit.Test; -/** - * Created by MarkPerry on 17/01/2015. - */ public class UnitTest { @Test diff --git a/core/src/test/java/fj/function/TestEffect.java b/core/src/test/java/fj/function/TestEffect.java index 00da26f0..de2f5c5f 100644 --- a/core/src/test/java/fj/function/TestEffect.java +++ b/core/src/test/java/fj/function/TestEffect.java @@ -1,11 +1,7 @@ package fj.function; -import fj.F; import org.junit.Test; -/** - * Created by mperry on 28/08/2014. - */ public class TestEffect { @Test diff --git a/demo/src/main/java/fj/demo/IODemo.java b/demo/src/main/java/fj/demo/IODemo.java index ea313e77..aad8e41b 100644 --- a/demo/src/main/java/fj/demo/IODemo.java +++ b/demo/src/main/java/fj/demo/IODemo.java @@ -10,9 +10,6 @@ import static fj.data.LazyString.unlines_; import static java.lang.System.out; -/** - * Created by MarkPerry on 13/06/2015. - */ public class IODemo { public static void main(String[] args) { diff --git a/demo/src/main/java/fj/demo/StateDemo_Greeter.java b/demo/src/main/java/fj/demo/StateDemo_Greeter.java index 578f414e..707a1f9f 100644 --- a/demo/src/main/java/fj/demo/StateDemo_Greeter.java +++ b/demo/src/main/java/fj/demo/StateDemo_Greeter.java @@ -3,9 +3,6 @@ import fj.P; import fj.data.State; -/** - * Created by mperry on 4/08/2014. - */ public class StateDemo_Greeter { public static void main(String args[]) { diff --git a/demo/src/main/java/fj/demo/StateDemo_VendingMachine.java b/demo/src/main/java/fj/demo/StateDemo_VendingMachine.java index ef1a09cd..75de4823 100644 --- a/demo/src/main/java/fj/demo/StateDemo_VendingMachine.java +++ b/demo/src/main/java/fj/demo/StateDemo_VendingMachine.java @@ -6,9 +6,6 @@ import static fj.demo.StateDemo_VendingMachine.Input.COIN; import static fj.demo.StateDemo_VendingMachine.Input.TURN; -/** - * Created by MarkPerry on 20/07/2014. - */ public class StateDemo_VendingMachine { public enum Input { COIN, TURN } diff --git a/demo/src/main/java/fj/demo/WriterDemo_Halver.java b/demo/src/main/java/fj/demo/WriterDemo_Halver.java index 8473e588..9e6c6dac 100644 --- a/demo/src/main/java/fj/demo/WriterDemo_Halver.java +++ b/demo/src/main/java/fj/demo/WriterDemo_Halver.java @@ -6,9 +6,6 @@ import static fj.Monoid.stringMonoid; -/** - * Created by mperry on 4/08/2014. - */ public class WriterDemo_Halver { public static void main(String args[]) { diff --git a/demo/src/main/java/fj/demo/optic/LensPerson.java b/demo/src/main/java/fj/demo/optic/LensPerson.java index 51f5e103..641543e9 100644 --- a/demo/src/main/java/fj/demo/optic/LensPerson.java +++ b/demo/src/main/java/fj/demo/optic/LensPerson.java @@ -6,9 +6,6 @@ import static org.junit.Assert.assertTrue; -/** - * Created by MarkPerry on 23/06/2015. - */ public class LensPerson { static final class Person { diff --git a/demo/src/main/java/fj/demo/realworld/Chapter7.java b/demo/src/main/java/fj/demo/realworld/Chapter7.java index c239a5bf..bc414e4f 100644 --- a/demo/src/main/java/fj/demo/realworld/Chapter7.java +++ b/demo/src/main/java/fj/demo/realworld/Chapter7.java @@ -8,8 +8,6 @@ import static fj.data.IOFunctions.*; /** - * Created by MarkPerry on 11/06/2015. - * * Examples from Chapter 7 of Real World Haskell, http://book.realworldhaskell.org/. * * Currently just ch07/toupper-lazy4.hs. diff --git a/demo/src/test/java/fj/EmptyTest.java b/demo/src/test/java/fj/EmptyTest.java index e112a97a..13675822 100644 --- a/demo/src/test/java/fj/EmptyTest.java +++ b/demo/src/test/java/fj/EmptyTest.java @@ -5,9 +5,6 @@ import org.junit.Assert; -/** - * Created by MarkPerry on 30/08/2015. - */ public class EmptyTest { @Ignore @Test diff --git a/java-core/src/main/java/fj/java/util/ListUtil.java b/java-core/src/main/java/fj/java/util/ListUtil.java index 17fb5519..ffab7968 100644 --- a/java-core/src/main/java/fj/java/util/ListUtil.java +++ b/java-core/src/main/java/fj/java/util/ListUtil.java @@ -5,9 +5,6 @@ import java.util.List; -/** - * Created by MarkPerry on 28/08/2015. - */ public class ListUtil { public static List map(List list, F f) { diff --git a/java-core/src/test/java/fj/EmptyTest.java b/java-core/src/test/java/fj/EmptyTest.java index d1088c03..1c126bfa 100644 --- a/java-core/src/test/java/fj/EmptyTest.java +++ b/java-core/src/test/java/fj/EmptyTest.java @@ -4,9 +4,6 @@ import org.junit.Test; import org.junit.Assert; -/** - * Created by MarkPerry on 30/08/2015. - */ public class EmptyTest { @Ignore @Test diff --git a/performance/src/main/java/fj/data/dummy/DummyClass.java b/performance/src/main/java/fj/data/dummy/DummyClass.java index 72cc5c34..a94dcdde 100644 --- a/performance/src/main/java/fj/data/dummy/DummyClass.java +++ b/performance/src/main/java/fj/data/dummy/DummyClass.java @@ -1,8 +1,5 @@ package fj.data.dummy; -/** - * Created by MarkPerry on 5 Feb 16. - */ public class DummyClass { } diff --git a/performance/src/test/java/fj/data/dummy/DummyTest.java b/performance/src/test/java/fj/data/dummy/DummyTest.java index d3865433..49722bcd 100644 --- a/performance/src/test/java/fj/data/dummy/DummyTest.java +++ b/performance/src/test/java/fj/data/dummy/DummyTest.java @@ -3,9 +3,6 @@ import org.junit.Ignore; import org.junit.Test; -/** - * Created by MarkPerry on 5 Feb 16. - */ public class DummyTest { @Test diff --git a/props-core/src/test/java/fj/MemoisationTest.java b/props-core/src/test/java/fj/MemoisationTest.java index 865f76a3..605546ef 100644 --- a/props-core/src/test/java/fj/MemoisationTest.java +++ b/props-core/src/test/java/fj/MemoisationTest.java @@ -12,9 +12,6 @@ import static fj.test.Property.property; import static org.junit.Assert.assertTrue; -/** - * Created by mperry on 14/07/2014. - */ @RunWith(PropertyTestRunner.class) public class MemoisationTest { diff --git a/props-core/src/test/java/fj/data/ReaderTest.java b/props-core/src/test/java/fj/data/ReaderTest.java index 044595d0..accc3394 100644 --- a/props-core/src/test/java/fj/data/ReaderTest.java +++ b/props-core/src/test/java/fj/data/ReaderTest.java @@ -13,9 +13,6 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -/** - * Created by MarkPerry on 4/12/2014. - */ public class ReaderTest { @Test diff --git a/props-core/src/test/java/fj/data/TestRngState.java b/props-core/src/test/java/fj/data/TestRngState.java index 5f5f81bb..41143c17 100644 --- a/props-core/src/test/java/fj/data/TestRngState.java +++ b/props-core/src/test/java/fj/data/TestRngState.java @@ -17,9 +17,6 @@ import static fj.test.Property.property; import static fj.test.Variant.variant; -/** - * Created by mperry on 4/08/2014. - */ public class TestRngState { static List expected1 = List.list(4,4,2,2,2,5,3,3,1,5); diff --git a/props-core/src/test/java/fj/data/WriterTest.java b/props-core/src/test/java/fj/data/WriterTest.java index 25f99b22..50134da3 100644 --- a/props-core/src/test/java/fj/data/WriterTest.java +++ b/props-core/src/test/java/fj/data/WriterTest.java @@ -16,9 +16,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -/** - * Created by MarkPerry on 17/12/2014. - */ public class WriterTest { @Test diff --git a/props-core/src/test/java/fj/data/fingertrees/FingerTreeProperties.java b/props-core/src/test/java/fj/data/fingertrees/FingerTreeProperties.java index b7ed83f5..446716c2 100644 --- a/props-core/src/test/java/fj/data/fingertrees/FingerTreeProperties.java +++ b/props-core/src/test/java/fj/data/fingertrees/FingerTreeProperties.java @@ -1,6 +1,5 @@ package fj.data.fingertrees; -import fj.P2; import fj.data.Stream; import fj.test.Property; import fj.test.reflect.CheckParams; @@ -12,9 +11,6 @@ import static fj.test.Property.prop; import static fj.test.Property.property; -/** - * Created by MarkPerry on 10/10/2015. - */ @RunWith(PropertyTestRunner.class) @CheckParams(maxSize = 10000) public class FingerTreeProperties { diff --git a/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java b/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java index b25fff51..a26fdc89 100644 --- a/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java +++ b/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java @@ -14,10 +14,6 @@ import static org.junit.Assert.assertThat; import static org.hamcrest.core.Is.is; - -/** - * Created by MarkPerry on 10/10/2015. - */ public class FingerTreeTest { public static final int SIZE = 10; diff --git a/props-core/src/test/java/fj/data/hamt/BitSetProperties.java b/props-core/src/test/java/fj/data/hamt/BitSetProperties.java index 6c3f91a9..fbda987b 100644 --- a/props-core/src/test/java/fj/data/hamt/BitSetProperties.java +++ b/props-core/src/test/java/fj/data/hamt/BitSetProperties.java @@ -31,10 +31,6 @@ import static fj.test.Property.prop; import static fj.test.Property.property; -/** - * Created by maperr on 31/05/2016. - */ - @RunWith(PropertyTestRunner.class) @CheckParams(maxSize = 10000) public class BitSetProperties { diff --git a/props-core/src/test/java/fj/data/properties/NonEmptyListProperties.java b/props-core/src/test/java/fj/data/properties/NonEmptyListProperties.java index 256fb3a8..64ce1934 100644 --- a/props-core/src/test/java/fj/data/properties/NonEmptyListProperties.java +++ b/props-core/src/test/java/fj/data/properties/NonEmptyListProperties.java @@ -25,9 +25,6 @@ import static fj.test.Property.property; import static java.lang.Math.min; -/** - * Created by Zheka Kozlov on 02.06.2015. - */ @RunWith(PropertyTestRunner.class) @CheckParams(maxSize = 10000) public class NonEmptyListProperties { diff --git a/props-core/src/test/java/fj/data/properties/PriorityQueueProperties.java b/props-core/src/test/java/fj/data/properties/PriorityQueueProperties.java index 2bde7e6e..c2e46eca 100644 --- a/props-core/src/test/java/fj/data/properties/PriorityQueueProperties.java +++ b/props-core/src/test/java/fj/data/properties/PriorityQueueProperties.java @@ -26,9 +26,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; -/** - * Created by MarkPerry on 18 Jun 16. - */ @RunWith(PropertyTestRunner.class) @CheckParams(maxSize = 100) public class PriorityQueueProperties { diff --git a/props-core/src/test/java/fj/data/properties/SetProperties.java b/props-core/src/test/java/fj/data/properties/SetProperties.java index 5349b650..2657b937 100644 --- a/props-core/src/test/java/fj/data/properties/SetProperties.java +++ b/props-core/src/test/java/fj/data/properties/SetProperties.java @@ -15,9 +15,6 @@ import static fj.test.Property.prop; import static fj.test.Property.property; -/** - * Created by MarkPerry on 18/08/2015. - */ @RunWith(PropertyTestRunner.class) @CheckParams(maxSize = 10000) public class SetProperties { diff --git a/props-core/src/test/java/fj/data/properties/TreeMapProperties.java b/props-core/src/test/java/fj/data/properties/TreeMapProperties.java index eb71749b..50b323ff 100644 --- a/props-core/src/test/java/fj/data/properties/TreeMapProperties.java +++ b/props-core/src/test/java/fj/data/properties/TreeMapProperties.java @@ -23,9 +23,6 @@ import static fj.test.Arbitrary.arbTreeMap; import static fj.test.Property.*; -/** - * Created by MarkPerry on 29/08/2015. - */ @RunWith(PropertyTestRunner.class) @CheckParams(maxSize = 10000) public class TreeMapProperties { diff --git a/props-core/src/test/java/fj/data/properties/ValidationProperties.java b/props-core/src/test/java/fj/data/properties/ValidationProperties.java index f5205d73..a47383ab 100644 --- a/props-core/src/test/java/fj/data/properties/ValidationProperties.java +++ b/props-core/src/test/java/fj/data/properties/ValidationProperties.java @@ -13,9 +13,6 @@ import static fj.test.Property.implies; import static fj.test.Property.prop; -/** - * Created by MarkPerry on 3/07/2015. - */ @RunWith(PropertyTestRunner.class) public class ValidationProperties { diff --git a/quickcheck/src/main/java/fj/data/test/PropertyAssert.java b/quickcheck/src/main/java/fj/data/test/PropertyAssert.java index 00187b15..d37c69a3 100644 --- a/quickcheck/src/main/java/fj/data/test/PropertyAssert.java +++ b/quickcheck/src/main/java/fj/data/test/PropertyAssert.java @@ -5,9 +5,6 @@ import fj.test.Property; import org.junit.Assert; -/** - * Created by MarkPerry on 18/12/2014. - */ public final class PropertyAssert { private PropertyAssert(){} diff --git a/quickcheck/src/test/java/fj/data/test/TestNull.java b/quickcheck/src/test/java/fj/data/test/TestNull.java index fe9dc843..0d547149 100644 --- a/quickcheck/src/test/java/fj/data/test/TestNull.java +++ b/quickcheck/src/test/java/fj/data/test/TestNull.java @@ -8,9 +8,6 @@ import static fj.test.Property.prop; import static fj.test.Property.property; -/** - * Created by MarkPerry on 3/07/2014. - */ public class TestNull { @Test diff --git a/quickcheck/src/test/java/fj/test/TestRand.java b/quickcheck/src/test/java/fj/test/TestRand.java index 1ca7c749..ea2cbce8 100644 --- a/quickcheck/src/test/java/fj/test/TestRand.java +++ b/quickcheck/src/test/java/fj/test/TestRand.java @@ -9,9 +9,6 @@ import static org.junit.Assert.assertTrue; -/** - * Created by MarkPerry on 4/06/2015. - */ public class TestRand { @Test From 8c8663a039959b07daf8c14c9a974dc5bf389960 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 11 Feb 2022 18:50:17 +1000 Subject: [PATCH 152/173] Updated release process --- etc/release-process.txt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/etc/release-process.txt b/etc/release-process.txt index b22ec4e4..3bfb7bbd 100644 --- a/etc/release-process.txt +++ b/etc/release-process.txt @@ -45,12 +45,10 @@ Setup Artifact Signing ====================== The below text is a summary from https://gist.github.com/phit/bd3c6d156a2fa5f3b1bc15fa94b3256c. -As of 2021-02-12, for Windows download Gpg4win 3.1.15 at https://gpg4win.org/index.html. You need to provide 3 things: -- the public key id -- the path to the secret key ring file for your private key -- the passphrase for your private key +As of 2022-02-11, for Windows download Gpg4win 3.1.16 from https://files.gpg4win.org/. Open a command prompt and run "gpg --gen-key" and follow the prompts. + Get your key id by running: "gpg --list-key" Example output: @@ -68,9 +66,9 @@ sub rsa2048 2017-06-17 [E] [expires: 2019-06-17] In this case we only have one key, 77273D57FA5140E5A91905087A1B92B81840D019 or short* 1840D019 which is basically just the last 8 characters of the long ID. -Export the key using "gpg --export-secret-key > %UserProfile%\secring.gpg" +Export the key using "gpg --export-secret-key >%UserProfile%\secring.gpg" -In %UserProfile%\.gradle\gradle.properties, set the values below: +Create the file %UserProfile%\.gradle\gradle.properties, set the values below: signing.keyId=XXXXXXXX signing.password=mypassword @@ -90,6 +88,7 @@ sub rsa3072 2021-02-12 [E] [expires: 2023-02-12] C:\repos\functionaljava>gpg --keyserver hkp://keyserver.ubuntu.com --send-keys E86A4EC34F25A9CF6118582A7985AAE03F41B2F9 gpg: sending key 7985AAE03F41B2F9 to hkp://keyserver.ubuntu.com + gradle upload (takes about 3 mins) From 3eea643f16a7f98127f7818c900f25a5752c1781 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 11 Feb 2022 20:56:10 +1000 Subject: [PATCH 153/173] Update release doc and Github readme --- README.adoc | 25 +++++++++++++------------ build.gradle | 1 - etc/release-process.txt | 10 ++++------ gradle.properties | 5 +++-- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/README.adoc b/README.adoc index cb53d003..d57b22b4 100644 --- a/README.adoc +++ b/README.adoc @@ -22,8 +22,9 @@ Important URLs for the project are: * Website, http://www.functionaljava.org * Website repository, http://github.com/functionaljava/functionaljava.github.io -* Travis continuous integration build, https://travis-ci.org/functionaljava/functionaljava -* Sonatype Repository, https://oss.sonatype.org/content/groups/public/org/functionaljava/ +* Travis continuous integration build, https://app.travis-ci.com/github/functionaljava/functionaljava +* Sonatype repository, https://oss.sonatype.org/content/groups/public/org/functionaljava/ +* Maven Central repository, https://mvnrepository.com/artifact/org.functionaljava/functionaljava == Downloading @@ -35,12 +36,12 @@ The Functional Java artifact is published to Maven Central using the group `org. * Java 8 specific support (`functionaljava-java8`) * property based testing (`functionaljava-quickcheck` or `functionaljava-quickcheck_1.8` if you use Java 8+) -The latest stable version is `4.8.1`. This can be added to your Gradle project by adding the dependencies: +The latest stable version is `4.9`. This can be added to your Gradle project by adding the dependencies: ---- -compile "org.functionaljava:functionaljava:4.8.1" -compile "org.functionaljava:functionaljava-java8:4.8.1" -compile "org.functionaljava:functionaljava-quickcheck:4.8.1" -compile "org.functionaljava:functionaljava-java-core:4.8.1" +compile "org.functionaljava:functionaljava:4.9" +compile "org.functionaljava:functionaljava-java8:4.9" +compile "org.functionaljava:functionaljava-quickcheck:4.9" +compile "org.functionaljava:functionaljava-java-core:4.9" ---- and in Maven: @@ -48,22 +49,22 @@ and in Maven: org.functionaljava functionaljava - 4.8.1 + 4.9 org.functionaljava functionaljava-java8 - 4.8.1 + 4.9 org.functionaljava functionaljava-quickcheck - 4.8.1 + 4.9 org.functionaljava functionaljava-java-core - 4.8.1 + 4.9 ---- @@ -71,7 +72,7 @@ and in Maven: FunctionalJava uses the Retro Lambda project to backport Java 8 lambdas to Java 6 bytecode. This requires access to both JDK 6 and 8. The build system requires the environment variables `JAVA6_HOME` and `JAVA8_HOME` to refer to the appropriate directories. -Building is done using Gradle 2.13. In the root directory run: +Building is done using Gradle 6.8.3. In the root directory run: ---- ./gradlew ---- diff --git a/build.gradle b/build.gradle index 1ceb4fb9..cc2e265c 100644 --- a/build.gradle +++ b/build.gradle @@ -41,7 +41,6 @@ allprojects { defaultTasks "build" ext { - isSnapshot = true fjBaseVersion = "5.0" snapshotAppendix = "-SNAPSHOT" diff --git a/etc/release-process.txt b/etc/release-process.txt index 3bfb7bbd..e4b35e2d 100644 --- a/etc/release-process.txt +++ b/etc/release-process.txt @@ -4,13 +4,11 @@ Current Release Process Go through the issues and pull requests and set the Label and Milestone field. Add information to /etc/release-notes/release-notes-.adoc. -Update build.gradle: -* set isSnapshot to false -* set useRetroLambda to true - Update gradle.properties: +* set isSnapshot to false * Set signingEnabled to true + Run the build command: gradlew clean build upload @@ -24,8 +22,8 @@ Login to Sonatype and verify the release: Commit changes Increase the version: -* Edit build.gradle: update isSnapshot to true, increase fjBaseVersion, update fjConsumeVersion, update useRetroLambda. -* Edit gradle.properties: set signingEnabled to false +* Edit build.gradle: increase fjBaseVersion, update fjConsumeVersion. +* Edit gradle.properties: set signingEnabled to false, set isSnapshot to true Commit changes and push. Notes that CI builds using Travis and Jenkins will fail with the release due to lack of configured signing. diff --git a/gradle.properties b/gradle.properties index 1f1f4ed9..1171b13a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,8 @@ +signingEnabled = false +isSnapshot = true + sonatypeUsername = incorrectUser sonatypePassword = incorrectPwd -signingEnabled = false - org.gradle.parallel = true From ce361b4466565cd4b074c7a11937f804b470bcba Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 11 Feb 2022 20:56:42 +1000 Subject: [PATCH 154/173] Re-added openjdk8 for FJ 5 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a7465305..fa0798a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ sudo: false language: java jdk: - - openjdk10 + - openjdk8 - openjdk11 - openjdk-ea From ba209425860e146c95386e9de314698c71e8e3cf Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 11 Feb 2022 21:05:36 +1000 Subject: [PATCH 155/173] Updated Travis CI link at the top of the readme --- README.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index d57b22b4..4ede16b7 100644 --- a/README.adoc +++ b/README.adoc @@ -1,6 +1,6 @@ = Functional Java -image:https://travis-ci.org/functionaljava/functionaljava.svg?branch=master["Build Status", link="https://travis-ci.org/functionaljava/functionaljava"] +image:https://travis-ci.org/functionaljava/functionaljava.svg?branch=master["Build Status", link="https://app.travis-ci.com/github/functionaljava/functionaljava"] image:https://codecov.io/gh/functionaljava/functionaljava/branch/master/graph/badge.svg["Code Coverage", link="https://codecov.io/gh/functionaljava/functionaljava"] image:https://badges.gitter.im/functionaljava/functionaljava.svg[link="https://gitter.im/functionaljava/functionaljava?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"] @@ -8,7 +8,7 @@ image::http://www.functionaljava.org/img/logo-600x144.png[] Functional Java is an open source library facilitating functional programming in Java. The library implements numerous basic and advanced programming abstractions that assist composition oriented development. Functional Java also serves as a platform for learning functional programming concepts by introducing these concepts using a familiar language. -The library is intended for use in production applications and is thoroughly tested using the technique of automated specification-based testing with ScalaCheck and Functional Java's quickcheck module. Functional Java is compiled with Java 8 targeting Java 7 bytecode. The use of lambdas within the project are backported with the Retro Lambda library, supporting Java versions 6 to 8. +The library is intended for use in production applications and is thoroughly tested using the technique of automated specification-based testing with ScalaCheck and Functional Java's quickcheck module. Functional Java is compiled with Java 8 targeting Java 7 bytecode. The use of lambdas within the project are back ported with the Retro Lambda library, supporting Java versions 6 to 8. Functional Java provides abstractions for the following types: From 80862115da1e0d065b95e394bd517851b314d821 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 11 Feb 2022 21:47:41 +1000 Subject: [PATCH 156/173] Prep for 5.0 release --- build.gradle | 1 + gradle.properties | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index cc2e265c..1e7bf98c 100644 --- a/build.gradle +++ b/build.gradle @@ -41,6 +41,7 @@ allprojects { defaultTasks "build" ext { + isSnapshot = false fjBaseVersion = "5.0" snapshotAppendix = "-SNAPSHOT" diff --git a/gradle.properties b/gradle.properties index 1171b13a..d18e6a6f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,5 @@ -signingEnabled = false -isSnapshot = true +signingEnabled = true sonatypeUsername = incorrectUser sonatypePassword = incorrectPwd From a4ff75ed351c2e0996399d2b059a76fa158317fc Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 11 Feb 2022 22:11:47 +1000 Subject: [PATCH 157/173] Prep for FJ 5.1 --- build.gradle | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 1e7bf98c..70485ec9 100644 --- a/build.gradle +++ b/build.gradle @@ -41,8 +41,8 @@ allprojects { defaultTasks "build" ext { - isSnapshot = false - fjBaseVersion = "5.0" + isSnapshot = true + fjBaseVersion = "5.1" snapshotAppendix = "-SNAPSHOT" fjVersion = fjBaseVersion + (isSnapshot ? snapshotAppendix : "") diff --git a/gradle.properties b/gradle.properties index d18e6a6f..50202eda 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ -signingEnabled = true +signingEnabled = false sonatypeUsername = incorrectUser sonatypePassword = incorrectPwd From 56a9a51594e1a4030ff27f4db345e7b736489c8d Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Sun, 13 Feb 2022 23:14:27 +1000 Subject: [PATCH 158/173] Added signing parameters to the properties file --- gradle.properties | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gradle.properties b/gradle.properties index 50202eda..dde4512b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,4 +4,8 @@ signingEnabled = false sonatypeUsername = incorrectUser sonatypePassword = incorrectPwd +signing.keyId= +signing.password= +signing.secretKeyRingFile= + org.gradle.parallel = true From 3c254bb3b6840512f896bb3348e3e5401837929e Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Mon, 14 Feb 2022 20:46:29 +1000 Subject: [PATCH 159/173] Fix series/5.x build. Removed extra jdk's for series 5.x. --- .travis.yml | 11 ----------- gradle.properties | 6 +++--- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index fa0798a4..bc3fc57f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,24 +5,13 @@ language: java jdk: - openjdk8 - - openjdk11 - - openjdk-ea matrix: fast_finish: true include: - - jdk: openjdk12 - script: - - ./gradlew build coverage -s -i - after_success: - - bash <(curl -s https://codecov.io/bash) - - '[ "$TRAVIS_BRANCH" = "series/5.x" -a "$TRAVIS_PULL_REQUEST" = "false" -a -z "$TRAVIS_TAG" ] - && ./gradlew uploadArchives' allow_failures: - - jdk: openjdk10 - - jdk: openjdk-ea script: - ./gradlew clean test diff --git a/gradle.properties b/gradle.properties index dde4512b..6ee9a768 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,8 +4,8 @@ signingEnabled = false sonatypeUsername = incorrectUser sonatypePassword = incorrectPwd -signing.keyId= -signing.password= -signing.secretKeyRingFile= +#signing.keyId= +#signing.password= +#signing.secretKeyRingFile= org.gradle.parallel = true From 474a49ca5b7760cd79243ca4b428ea7ddef0b72e Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Mon, 14 Feb 2022 22:18:36 +1000 Subject: [PATCH 160/173] Upgrade Junit to 4.13.2, fixed deprecations. Update gradle-versions-plugin, junit-vintage-engine --- build.gradle | 8 ++++---- core/src/test/java/fj/ClassTest.java | 2 +- core/src/test/java/fj/DigitTest.java | 2 +- core/src/test/java/fj/MonoidTest.java | 2 +- core/src/test/java/fj/TryTest.java | 2 +- core/src/test/java/fj/control/db/TestDbState.java | 2 +- core/src/test/java/fj/data/ArrayTest.java | 2 +- core/src/test/java/fj/data/DListTest.java | 2 +- core/src/test/java/fj/data/LazyStringTest.java | 2 +- core/src/test/java/fj/data/ListBufferTest.java | 2 +- core/src/test/java/fj/data/SetTest.java | 2 +- core/src/test/java/fj/data/StreamTest.java | 2 +- core/src/test/java/fj/data/TreeMapTest.java | 2 +- core/src/test/java/fj/data/TreeTest.java | 2 +- core/src/test/java/fj/data/TreeZipperTest.java | 2 +- core/src/test/java/fj/data/ValidationTest.java | 2 +- core/src/test/java/fj/data/ZipperTest.java | 2 +- core/src/test/java/fj/data/hamt/HamtTest.java | 2 +- core/src/test/java/fj/data/optic/IsoTest.java | 2 +- core/src/test/java/fj/data/optic/LensTest.java | 2 +- core/src/test/java/fj/data/optic/OptionalTest.java | 2 +- core/src/test/java/fj/data/optic/PrismTest.java | 2 +- core/src/test/java/fj/data/optic/TraversalTest.java | 2 +- core/src/test/java/fj/data/vector/VTest.java | 2 +- core/src/test/java/fj/function/VisitorTest.java | 2 +- props-core/src/test/java/fj/data/ReaderTest.java | 2 +- .../src/test/java/fj/data/fingertrees/FingerTreeTest.java | 3 +-- .../java/fj/data/properties/PriorityQueueProperties.java | 2 +- 28 files changed, 31 insertions(+), 32 deletions(-) diff --git a/build.gradle b/build.gradle index 70485ec9..93dafa67 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { } dependencies { - classpath "com.github.ben-manes:gradle-versions-plugin:0.27.0" + classpath "com.github.ben-manes:gradle-versions-plugin:0.36.0" classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:5.3.0" } @@ -46,7 +46,7 @@ allprojects { snapshotAppendix = "-SNAPSHOT" fjVersion = fjBaseVersion + (isSnapshot ? snapshotAppendix : "") - fjConsumeVersion = "4.9" + fjConsumeVersion = "5.0" signModule = false @@ -66,8 +66,8 @@ allprojects { sonatypeUploadUrl = isSnapshot ? sonatypeSnapshotUrl : sonatypeReleaseUrl primaryEmail = "functionaljava@googlegroups.com" - junitCompile = "junit:junit:4.12" - junitRuntime = "org.junit.vintage:junit-vintage-engine:5.5.2" + junitCompile = "junit:junit:4.13.2" + junitRuntime = "org.junit.vintage:junit-vintage-engine:5.8.2" displayCompilerWarnings = true generateTestReports = false diff --git a/core/src/test/java/fj/ClassTest.java b/core/src/test/java/fj/ClassTest.java index 2959e351..dfbc7f00 100644 --- a/core/src/test/java/fj/ClassTest.java +++ b/core/src/test/java/fj/ClassTest.java @@ -11,7 +11,7 @@ import java.util.Iterator; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class ClassTest { @Test diff --git a/core/src/test/java/fj/DigitTest.java b/core/src/test/java/fj/DigitTest.java index 9fc98d4c..13169aca 100644 --- a/core/src/test/java/fj/DigitTest.java +++ b/core/src/test/java/fj/DigitTest.java @@ -5,7 +5,7 @@ import static fj.data.Array.range; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class DigitTest { @Test diff --git a/core/src/test/java/fj/MonoidTest.java b/core/src/test/java/fj/MonoidTest.java index 1156e729..68457f09 100644 --- a/core/src/test/java/fj/MonoidTest.java +++ b/core/src/test/java/fj/MonoidTest.java @@ -8,7 +8,7 @@ import static fj.data.Option.some; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class MonoidTest { diff --git a/core/src/test/java/fj/TryTest.java b/core/src/test/java/fj/TryTest.java index bd94e986..46fb2dd3 100644 --- a/core/src/test/java/fj/TryTest.java +++ b/core/src/test/java/fj/TryTest.java @@ -5,7 +5,7 @@ import org.junit.Test; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class TryTest { diff --git a/core/src/test/java/fj/control/db/TestDbState.java b/core/src/test/java/fj/control/db/TestDbState.java index 188eae3d..2a942e1a 100644 --- a/core/src/test/java/fj/control/db/TestDbState.java +++ b/core/src/test/java/fj/control/db/TestDbState.java @@ -9,7 +9,7 @@ import java.sql.*; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class TestDbState { @Test diff --git a/core/src/test/java/fj/data/ArrayTest.java b/core/src/test/java/fj/data/ArrayTest.java index 5713e5c3..e686a02b 100644 --- a/core/src/test/java/fj/data/ArrayTest.java +++ b/core/src/test/java/fj/data/ArrayTest.java @@ -5,7 +5,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class ArrayTest { diff --git a/core/src/test/java/fj/data/DListTest.java b/core/src/test/java/fj/data/DListTest.java index 6846ca2d..48c0c2fe 100644 --- a/core/src/test/java/fj/data/DListTest.java +++ b/core/src/test/java/fj/data/DListTest.java @@ -4,7 +4,7 @@ import static fj.data.DList.*; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class DListTest { @Test diff --git a/core/src/test/java/fj/data/LazyStringTest.java b/core/src/test/java/fj/data/LazyStringTest.java index 1adef2bd..14a88c14 100644 --- a/core/src/test/java/fj/data/LazyStringTest.java +++ b/core/src/test/java/fj/data/LazyStringTest.java @@ -4,7 +4,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class LazyStringTest { diff --git a/core/src/test/java/fj/data/ListBufferTest.java b/core/src/test/java/fj/data/ListBufferTest.java index f2ed8286..6b16f1ca 100644 --- a/core/src/test/java/fj/data/ListBufferTest.java +++ b/core/src/test/java/fj/data/ListBufferTest.java @@ -3,7 +3,7 @@ import org.junit.Test; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class ListBufferTest { diff --git a/core/src/test/java/fj/data/SetTest.java b/core/src/test/java/fj/data/SetTest.java index ae65b687..e30d166a 100644 --- a/core/src/test/java/fj/data/SetTest.java +++ b/core/src/test/java/fj/data/SetTest.java @@ -6,7 +6,7 @@ import static fj.data.Option.some; import static fj.Ord.intOrd; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class SetTest { diff --git a/core/src/test/java/fj/data/StreamTest.java b/core/src/test/java/fj/data/StreamTest.java index f320f27a..febd4567 100644 --- a/core/src/test/java/fj/data/StreamTest.java +++ b/core/src/test/java/fj/data/StreamTest.java @@ -36,7 +36,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class StreamTest { diff --git a/core/src/test/java/fj/data/TreeMapTest.java b/core/src/test/java/fj/data/TreeMapTest.java index 77352989..051f0773 100644 --- a/core/src/test/java/fj/data/TreeMapTest.java +++ b/core/src/test/java/fj/data/TreeMapTest.java @@ -16,7 +16,7 @@ import static fj.data.TreeMap.iterableTreeMap; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; public class TreeMapTest { diff --git a/core/src/test/java/fj/data/TreeTest.java b/core/src/test/java/fj/data/TreeTest.java index fd43c852..a6028f37 100644 --- a/core/src/test/java/fj/data/TreeTest.java +++ b/core/src/test/java/fj/data/TreeTest.java @@ -4,7 +4,7 @@ import static fj.data.Tree.leaf; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class TreeTest { diff --git a/core/src/test/java/fj/data/TreeZipperTest.java b/core/src/test/java/fj/data/TreeZipperTest.java index c52f4111..c2549963 100644 --- a/core/src/test/java/fj/data/TreeZipperTest.java +++ b/core/src/test/java/fj/data/TreeZipperTest.java @@ -4,7 +4,7 @@ import static fj.data.Option.none; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class TreeZipperTest { @Test diff --git a/core/src/test/java/fj/data/ValidationTest.java b/core/src/test/java/fj/data/ValidationTest.java index 37a51bcc..4505499c 100644 --- a/core/src/test/java/fj/data/ValidationTest.java +++ b/core/src/test/java/fj/data/ValidationTest.java @@ -37,7 +37,7 @@ import static fj.data.Validation.*; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class ValidationTest { diff --git a/core/src/test/java/fj/data/ZipperTest.java b/core/src/test/java/fj/data/ZipperTest.java index 51c7b4ea..78d837aa 100644 --- a/core/src/test/java/fj/data/ZipperTest.java +++ b/core/src/test/java/fj/data/ZipperTest.java @@ -3,7 +3,7 @@ import org.junit.Test; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class ZipperTest { @Test diff --git a/core/src/test/java/fj/data/hamt/HamtTest.java b/core/src/test/java/fj/data/hamt/HamtTest.java index 1e601f24..19a22411 100644 --- a/core/src/test/java/fj/data/hamt/HamtTest.java +++ b/core/src/test/java/fj/data/hamt/HamtTest.java @@ -12,7 +12,7 @@ import static fj.P.p; import static fj.data.List.list; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; /** * @author Mark Perry diff --git a/core/src/test/java/fj/data/optic/IsoTest.java b/core/src/test/java/fj/data/optic/IsoTest.java index 6cace533..7aace4d8 100644 --- a/core/src/test/java/fj/data/optic/IsoTest.java +++ b/core/src/test/java/fj/data/optic/IsoTest.java @@ -5,7 +5,7 @@ import org.junit.Test; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class IsoTest { @Test diff --git a/core/src/test/java/fj/data/optic/LensTest.java b/core/src/test/java/fj/data/optic/LensTest.java index d78fa0d0..60513651 100644 --- a/core/src/test/java/fj/data/optic/LensTest.java +++ b/core/src/test/java/fj/data/optic/LensTest.java @@ -4,7 +4,7 @@ import org.junit.Test; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class LensTest { @Test diff --git a/core/src/test/java/fj/data/optic/OptionalTest.java b/core/src/test/java/fj/data/optic/OptionalTest.java index 07c3f79a..5bea2864 100644 --- a/core/src/test/java/fj/data/optic/OptionalTest.java +++ b/core/src/test/java/fj/data/optic/OptionalTest.java @@ -4,7 +4,7 @@ import org.junit.Test; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class OptionalTest { @Test diff --git a/core/src/test/java/fj/data/optic/PrismTest.java b/core/src/test/java/fj/data/optic/PrismTest.java index da128d72..ce6dddb4 100644 --- a/core/src/test/java/fj/data/optic/PrismTest.java +++ b/core/src/test/java/fj/data/optic/PrismTest.java @@ -4,7 +4,7 @@ import org.junit.Test; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class PrismTest { @Test diff --git a/core/src/test/java/fj/data/optic/TraversalTest.java b/core/src/test/java/fj/data/optic/TraversalTest.java index cadd6ccf..9cfc25cc 100644 --- a/core/src/test/java/fj/data/optic/TraversalTest.java +++ b/core/src/test/java/fj/data/optic/TraversalTest.java @@ -5,7 +5,7 @@ import org.junit.Test; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class TraversalTest { @Test diff --git a/core/src/test/java/fj/data/vector/VTest.java b/core/src/test/java/fj/data/vector/VTest.java index 9a3709ac..8dabd66e 100644 --- a/core/src/test/java/fj/data/vector/VTest.java +++ b/core/src/test/java/fj/data/vector/VTest.java @@ -5,7 +5,7 @@ import org.junit.Test; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class VTest { @Test diff --git a/core/src/test/java/fj/function/VisitorTest.java b/core/src/test/java/fj/function/VisitorTest.java index 54efcf24..da92a4bb 100644 --- a/core/src/test/java/fj/function/VisitorTest.java +++ b/core/src/test/java/fj/function/VisitorTest.java @@ -11,7 +11,7 @@ import static fj.data.Option.some; import static fj.function.Visitor.*; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class VisitorTest { @Test diff --git a/props-core/src/test/java/fj/data/ReaderTest.java b/props-core/src/test/java/fj/data/ReaderTest.java index accc3394..4817e41c 100644 --- a/props-core/src/test/java/fj/data/ReaderTest.java +++ b/props-core/src/test/java/fj/data/ReaderTest.java @@ -10,7 +10,7 @@ import static fj.test.Property.prop; import static fj.test.Property.property; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; public class ReaderTest { diff --git a/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java b/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java index a26fdc89..7f02288c 100644 --- a/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java +++ b/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java @@ -8,10 +8,9 @@ import org.junit.Test; import static fj.Monoid.intAdditionMonoid; -import static fj.Monoid.intMinMonoid; import static fj.data.fingertrees.FingerTree.measured; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; public class FingerTreeTest { diff --git a/props-core/src/test/java/fj/data/properties/PriorityQueueProperties.java b/props-core/src/test/java/fj/data/properties/PriorityQueueProperties.java index c2e46eca..5460bfdf 100644 --- a/props-core/src/test/java/fj/data/properties/PriorityQueueProperties.java +++ b/props-core/src/test/java/fj/data/properties/PriorityQueueProperties.java @@ -24,7 +24,7 @@ import static fj.test.Property.prop; import static fj.test.Property.property; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; @RunWith(PropertyTestRunner.class) @CheckParams(maxSize = 100) From c4ebbb541a40bcbf8a17e6ef1f1cae7a3a463ff4 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Mon, 14 Feb 2022 22:22:45 +1000 Subject: [PATCH 161/173] Updated bnd to 6.1.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 93dafa67..3b627e85 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { dependencies { classpath "com.github.ben-manes:gradle-versions-plugin:0.36.0" - classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:5.3.0" + classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:6.1.0" } wrapper { From df6e6f8ea2fee836794c365f93fc8839388fd02b Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Mon, 14 Feb 2022 22:49:54 +1000 Subject: [PATCH 162/173] Updated H2 lib to 2.1.210. Updated deprecated Assert.assertThat to MatcherAssert.assertThat --- core/build.gradle | 2 +- core/src/test/java/fj/FFunctionsTest.java | 2 +- core/src/test/java/fj/OrderingTest.java | 2 +- core/src/test/java/fj/PTest.java | 2 +- core/src/test/java/fj/TryEffectTest.java | 2 +- .../test/java/fj/control/parallel/StrategyTest.java | 3 ++- core/src/test/java/fj/data/BooleansTest.java | 3 ++- core/src/test/java/fj/data/IOFunctionsTest.java | 11 ++++++----- core/src/test/java/fj/data/ListTest.java | 5 ++++- core/src/test/java/fj/data/List_Traverse_Tests.java | 2 +- core/src/test/java/fj/function/DoublesTest.java | 5 ++++- core/src/test/java/fj/function/IntegersTest.java | 5 ++++- core/src/test/java/fj/function/LongsTest.java | 5 ++++- core/src/test/java/fj/function/StringsTest.java | 2 +- core/src/test/java/fj/parser/ParserTest.java | 2 +- 15 files changed, 34 insertions(+), 19 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index cbdad60b..9ee293a9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -8,7 +8,7 @@ archivesBaseName = project.projectName dependencies { testCompile junitCompile testRuntime junitRuntime - testCompile 'com.h2database:h2:1.4.199' + testCompile 'com.h2database:h2:2.1.210' testCompile 'commons-dbutils:commons-dbutils:1.7' } diff --git a/core/src/test/java/fj/FFunctionsTest.java b/core/src/test/java/fj/FFunctionsTest.java index b235ff67..4f01a0db 100644 --- a/core/src/test/java/fj/FFunctionsTest.java +++ b/core/src/test/java/fj/FFunctionsTest.java @@ -4,7 +4,7 @@ import fj.data.TreeZipper; import org.junit.Test; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; public class FFunctionsTest { diff --git a/core/src/test/java/fj/OrderingTest.java b/core/src/test/java/fj/OrderingTest.java index 9b980e67..640bc1d3 100644 --- a/core/src/test/java/fj/OrderingTest.java +++ b/core/src/test/java/fj/OrderingTest.java @@ -8,7 +8,7 @@ import static fj.Ordering.GT; import static fj.Ordering.LT; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; public class OrderingTest { diff --git a/core/src/test/java/fj/PTest.java b/core/src/test/java/fj/PTest.java index 2a97303f..00220f9d 100644 --- a/core/src/test/java/fj/PTest.java +++ b/core/src/test/java/fj/PTest.java @@ -4,7 +4,7 @@ import static fj.Function.identity; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class PTest { @Test diff --git a/core/src/test/java/fj/TryEffectTest.java b/core/src/test/java/fj/TryEffectTest.java index ea92b8f2..6fd46fd1 100644 --- a/core/src/test/java/fj/TryEffectTest.java +++ b/core/src/test/java/fj/TryEffectTest.java @@ -5,7 +5,7 @@ import fj.function.TryEffect1; import org.junit.Test; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; public class TryEffectTest { diff --git a/core/src/test/java/fj/control/parallel/StrategyTest.java b/core/src/test/java/fj/control/parallel/StrategyTest.java index 0c605829..b4d21320 100644 --- a/core/src/test/java/fj/control/parallel/StrategyTest.java +++ b/core/src/test/java/fj/control/parallel/StrategyTest.java @@ -16,7 +16,8 @@ import static fj.control.parallel.Strategy.*; import static fj.data.Stream.range; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; + public class StrategyTest { diff --git a/core/src/test/java/fj/data/BooleansTest.java b/core/src/test/java/fj/data/BooleansTest.java index 84f7676f..61325df0 100644 --- a/core/src/test/java/fj/data/BooleansTest.java +++ b/core/src/test/java/fj/data/BooleansTest.java @@ -9,6 +9,7 @@ import static fj.data.List.list; import static fj.function.Booleans.isnot; import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.assertThat; public class BooleansTest { @@ -70,7 +71,7 @@ public void testIsNot(){ F f1 = a -> a == 4; List result = list("some", "come", "done!").filter(isnot(String::length, f1)); - Assert.assertThat(result.length(), is(1)); + assertThat(result.length(), is(1)); Assert.assertEquals(result, list("done!")); } diff --git a/core/src/test/java/fj/data/IOFunctionsTest.java b/core/src/test/java/fj/data/IOFunctionsTest.java index adad0d11..8ae18b1a 100644 --- a/core/src/test/java/fj/data/IOFunctionsTest.java +++ b/core/src/test/java/fj/data/IOFunctionsTest.java @@ -12,7 +12,8 @@ import static fj.data.Stream.cons; import static fj.data.Stream.nil_; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; public class IOFunctionsTest { @@ -33,8 +34,8 @@ public void close() { r -> () -> new BufferedReader(r).readLine() ); - Assert.assertThat(bracketed.run(), is("Read OK")); - Assert.assertThat(closed.get(), is(true)); + assertThat(bracketed.run(), is("Read OK")); + assertThat(closed.get(), is(true)); } @Test @@ -59,9 +60,9 @@ public void close() { bracketed.run(); fail("Exception expected"); } catch (IllegalArgumentException e) { - Assert.assertThat(e.getMessage(), is("OoO")); + assertThat(e.getMessage(), is("OoO")); } - Assert.assertThat(closed.get(), is(true)); + assertThat(closed.get(), is(true)); } @Test diff --git a/core/src/test/java/fj/data/ListTest.java b/core/src/test/java/fj/data/ListTest.java index 9f09de0f..720157e8 100644 --- a/core/src/test/java/fj/data/ListTest.java +++ b/core/src/test/java/fj/data/ListTest.java @@ -30,7 +30,10 @@ import static fj.data.Validation.fail; import static fj.data.Validation.*; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; public class ListTest { diff --git a/core/src/test/java/fj/data/List_Traverse_Tests.java b/core/src/test/java/fj/data/List_Traverse_Tests.java index 5b7c0d19..ad925847 100644 --- a/core/src/test/java/fj/data/List_Traverse_Tests.java +++ b/core/src/test/java/fj/data/List_Traverse_Tests.java @@ -7,7 +7,7 @@ import static fj.data.List.list; import static fj.data.Option.some; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class List_Traverse_Tests { diff --git a/core/src/test/java/fj/function/DoublesTest.java b/core/src/test/java/fj/function/DoublesTest.java index 6ba98fa2..09ca4bbe 100644 --- a/core/src/test/java/fj/function/DoublesTest.java +++ b/core/src/test/java/fj/function/DoublesTest.java @@ -1,6 +1,9 @@ package fj.function; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import fj.F; import org.junit.Test; diff --git a/core/src/test/java/fj/function/IntegersTest.java b/core/src/test/java/fj/function/IntegersTest.java index 985afba9..c07197af 100644 --- a/core/src/test/java/fj/function/IntegersTest.java +++ b/core/src/test/java/fj/function/IntegersTest.java @@ -8,7 +8,10 @@ import static fj.data.List.list; import static fj.data.Option.none; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; public class IntegersTest { diff --git a/core/src/test/java/fj/function/LongsTest.java b/core/src/test/java/fj/function/LongsTest.java index d59fc07c..196da20f 100644 --- a/core/src/test/java/fj/function/LongsTest.java +++ b/core/src/test/java/fj/function/LongsTest.java @@ -8,7 +8,10 @@ import static fj.data.List.list; import static fj.data.Option.none; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + public class LongsTest { diff --git a/core/src/test/java/fj/function/StringsTest.java b/core/src/test/java/fj/function/StringsTest.java index c13e1b81..79cb8a3d 100644 --- a/core/src/test/java/fj/function/StringsTest.java +++ b/core/src/test/java/fj/function/StringsTest.java @@ -5,7 +5,7 @@ import static fj.Function.compose; import static fj.function.Strings.*; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; public class StringsTest { diff --git a/core/src/test/java/fj/parser/ParserTest.java b/core/src/test/java/fj/parser/ParserTest.java index a3f623f5..f2664a1f 100644 --- a/core/src/test/java/fj/parser/ParserTest.java +++ b/core/src/test/java/fj/parser/ParserTest.java @@ -8,7 +8,7 @@ import static fj.parser.Result.result; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; public class ParserTest { @Test From 5ecd37c0b30b383ed8788b62190b4efce6b0feab Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Mon, 14 Feb 2022 23:54:44 +1000 Subject: [PATCH 163/173] Upgrade Gradle to 7.3, jacoco to 0.8.7. Upgrade plugin from java to java-library, maven to maven-publish --- build.gradle | 19 ++- consume/build.gradle | 6 +- core/build.gradle | 14 +- .../fj/control/parallel/StrategyTest.java | 1 - core/src/test/java/fj/data/ArrayTest.java | 1 + core/src/test/java/fj/data/BooleansTest.java | 1 + core/src/test/java/fj/data/DListTest.java | 1 + .../src/test/java/fj/data/LazyStringTest.java | 1 + .../src/test/java/fj/data/ListBufferTest.java | 1 + .../java/fj/data/List_Traverse_Tests.java | 1 + core/src/test/java/fj/data/hamt/HamtTest.java | 1 + core/src/test/java/fj/data/optic/IsoTest.java | 1 + .../src/test/java/fj/data/optic/LensTest.java | 1 + .../test/java/fj/data/optic/OptionalTest.java | 1 + core/src/test/java/fj/data/vector/VTest.java | 1 + .../test/java/fj/function/DoublesTest.java | 2 +- .../test/java/fj/function/StringsTest.java | 4 +- demo/build.gradle | 8 +- gradle/wrapper/gradle-wrapper.properties | 2 +- java-core/build.gradle | 11 +- lib.gradle | 138 +++++++++++++----- performance/build.gradle | 6 +- props-core-scalacheck/build.gradle | 12 +- props-core/build.gradle | 6 +- .../src/test/java/fj/data/ReaderTest.java | 1 + .../fj/data/fingertrees/FingerTreeTest.java | 3 +- quickcheck/build.gradle | 9 +- 27 files changed, 173 insertions(+), 80 deletions(-) diff --git a/build.gradle b/build.gradle index 3b627e85..9556b473 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { } wrapper { - gradleVersion = "6.8.3" + gradleVersion = "7.3" distributionType = Wrapper.DistributionType.ALL } } @@ -35,7 +35,7 @@ allprojects { apply plugin: "jacoco" jacoco { - toolVersion = "0.8.2" + toolVersion = "0.8.7" } defaultTasks "build" @@ -49,6 +49,7 @@ allprojects { fjConsumeVersion = "5.0" signModule = false + uploadModule = false projectTitle = "Functional Java" projectName = "functionaljava" @@ -58,12 +59,20 @@ allprojects { projectUrl = "http://functionaljava.org/" scmUrl = "git://github.com/functionaljava/functionaljava.git" scmGitFile = "scm:git@github.com:functionaljava/functionaljava.git" + scmSshGitFile = "scm:git:ssh://git@github.com/functionaljava/functionaljava.git" + licenseUrl = "https://github.com/functionaljava/functionaljava/blob/master/etc/LICENCE" + licenseName = "The BSD3 License" + + issueUrl = "https://github.com/functionaljava/functionaljava/issues" + githubUrl = "https://github.com/functionaljava/functionaljava" sonatypeBaseUrl = "https://oss.sonatype.org" sonatypeSnapshotUrl = "$sonatypeBaseUrl/content/repositories/snapshots/" sonatypeRepositoryUrl = "$sonatypeBaseUrl/content/groups/public" sonatypeReleaseUrl = "$sonatypeBaseUrl/service/local/staging/deploy/maven2/" + sonatypeUploadUrl = isSnapshot ? sonatypeSnapshotUrl : sonatypeReleaseUrl + primaryEmail = "functionaljava@googlegroups.com" junitCompile = "junit:junit:4.13.2" @@ -94,7 +103,7 @@ subprojects { } apply from: "$rootDir/lib.gradle" - apply plugin: "java" + apply plugin: "java-library" apply plugin: "eclipse" repositories { @@ -139,7 +148,7 @@ task coverage(type: org.gradle.testing.jacoco.tasks.JacocoReport) { configure(subprojects.findAll { it.name != "props-core" }) { - apply plugin: "maven" + apply plugin: "maven-publish" apply plugin: "signing" apply plugin: "biz.aQute.bnd.builder" sourceCompatibility = "1.8" @@ -164,7 +173,7 @@ configure(subprojects.findAll { it.name != "props-core" }) { } jar { - version project.fjVersion + archiveVersion = project.fjVersion bnd ( 'Bundle-Name': 'Functional Java', 'Signature-Version': project.fjVersion, diff --git a/consume/build.gradle b/consume/build.gradle index 370ebfbf..45dadefb 100644 --- a/consume/build.gradle +++ b/consume/build.gradle @@ -2,8 +2,8 @@ archivesBaseName = "${project.projectName}-${project.name}" dependencies { - compile("$group:$projectName:$fjConsumeVersion") + api "$group:$projectName:$fjConsumeVersion" - testCompile junitCompile - testRuntime junitRuntime + testImplementation junitCompile + testRuntimeOnly junitRuntime } diff --git a/core/build.gradle b/core/build.gradle index 9ee293a9..282ec191 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,18 +1,18 @@ ext { signModule = true + uploadModule = true + } archivesBaseName = project.projectName dependencies { - testCompile junitCompile - testRuntime junitRuntime - testCompile 'com.h2database:h2:2.1.210' - testCompile 'commons-dbutils:commons-dbutils:1.7' + testImplementation junitCompile + testRuntimeOnly junitRuntime + testImplementation 'com.h2database:h2:2.1.210' + testImplementation 'commons-dbutils:commons-dbutils:1.7' } performSigning(signingEnabled, signModule) -configureUpload(signingEnabled, signModule) - -uploadArchives.enabled = true +configureUpload(signingEnabled, signModule, uploadModule) diff --git a/core/src/test/java/fj/control/parallel/StrategyTest.java b/core/src/test/java/fj/control/parallel/StrategyTest.java index b4d21320..1f5e7bba 100644 --- a/core/src/test/java/fj/control/parallel/StrategyTest.java +++ b/core/src/test/java/fj/control/parallel/StrategyTest.java @@ -18,7 +18,6 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.MatcherAssert.assertThat; - public class StrategyTest { @Test diff --git a/core/src/test/java/fj/data/ArrayTest.java b/core/src/test/java/fj/data/ArrayTest.java index e686a02b..11683fbf 100644 --- a/core/src/test/java/fj/data/ArrayTest.java +++ b/core/src/test/java/fj/data/ArrayTest.java @@ -7,6 +7,7 @@ import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; + public class ArrayTest { @Test diff --git a/core/src/test/java/fj/data/BooleansTest.java b/core/src/test/java/fj/data/BooleansTest.java index 61325df0..e5dbeb12 100644 --- a/core/src/test/java/fj/data/BooleansTest.java +++ b/core/src/test/java/fj/data/BooleansTest.java @@ -11,6 +11,7 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.MatcherAssert.assertThat; + public class BooleansTest { @Test diff --git a/core/src/test/java/fj/data/DListTest.java b/core/src/test/java/fj/data/DListTest.java index 48c0c2fe..77f4db6c 100644 --- a/core/src/test/java/fj/data/DListTest.java +++ b/core/src/test/java/fj/data/DListTest.java @@ -6,6 +6,7 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.MatcherAssert.assertThat; + public class DListTest { @Test public void testConsSnoc() { diff --git a/core/src/test/java/fj/data/LazyStringTest.java b/core/src/test/java/fj/data/LazyStringTest.java index 14a88c14..060c5394 100644 --- a/core/src/test/java/fj/data/LazyStringTest.java +++ b/core/src/test/java/fj/data/LazyStringTest.java @@ -6,6 +6,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; + public class LazyStringTest { @Test diff --git a/core/src/test/java/fj/data/ListBufferTest.java b/core/src/test/java/fj/data/ListBufferTest.java index 6b16f1ca..784b9510 100644 --- a/core/src/test/java/fj/data/ListBufferTest.java +++ b/core/src/test/java/fj/data/ListBufferTest.java @@ -5,6 +5,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; + public class ListBufferTest { @Test diff --git a/core/src/test/java/fj/data/List_Traverse_Tests.java b/core/src/test/java/fj/data/List_Traverse_Tests.java index ad925847..eee5752d 100644 --- a/core/src/test/java/fj/data/List_Traverse_Tests.java +++ b/core/src/test/java/fj/data/List_Traverse_Tests.java @@ -9,6 +9,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; + public class List_Traverse_Tests { @Test diff --git a/core/src/test/java/fj/data/hamt/HamtTest.java b/core/src/test/java/fj/data/hamt/HamtTest.java index 19a22411..ce901e6e 100644 --- a/core/src/test/java/fj/data/hamt/HamtTest.java +++ b/core/src/test/java/fj/data/hamt/HamtTest.java @@ -14,6 +14,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; + /** * @author Mark Perry */ diff --git a/core/src/test/java/fj/data/optic/IsoTest.java b/core/src/test/java/fj/data/optic/IsoTest.java index 7aace4d8..f9559d3f 100644 --- a/core/src/test/java/fj/data/optic/IsoTest.java +++ b/core/src/test/java/fj/data/optic/IsoTest.java @@ -7,6 +7,7 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.MatcherAssert.assertThat; + public class IsoTest { @Test public void testIso() { diff --git a/core/src/test/java/fj/data/optic/LensTest.java b/core/src/test/java/fj/data/optic/LensTest.java index 60513651..98be7fca 100644 --- a/core/src/test/java/fj/data/optic/LensTest.java +++ b/core/src/test/java/fj/data/optic/LensTest.java @@ -6,6 +6,7 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.MatcherAssert.assertThat; + public class LensTest { @Test public void testLensPersonGet() { diff --git a/core/src/test/java/fj/data/optic/OptionalTest.java b/core/src/test/java/fj/data/optic/OptionalTest.java index 5bea2864..7fddb31b 100644 --- a/core/src/test/java/fj/data/optic/OptionalTest.java +++ b/core/src/test/java/fj/data/optic/OptionalTest.java @@ -6,6 +6,7 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.MatcherAssert.assertThat; + public class OptionalTest { @Test public void testOptionalSome() { diff --git a/core/src/test/java/fj/data/vector/VTest.java b/core/src/test/java/fj/data/vector/VTest.java index 8dabd66e..34f8686c 100644 --- a/core/src/test/java/fj/data/vector/VTest.java +++ b/core/src/test/java/fj/data/vector/VTest.java @@ -7,6 +7,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; + public class VTest { @Test public void testVectorUp(){ diff --git a/core/src/test/java/fj/function/DoublesTest.java b/core/src/test/java/fj/function/DoublesTest.java index 09ca4bbe..53b74e52 100644 --- a/core/src/test/java/fj/function/DoublesTest.java +++ b/core/src/test/java/fj/function/DoublesTest.java @@ -1,7 +1,6 @@ package fj.function; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -12,6 +11,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import static org.junit.Assert.assertTrue; import static org.hamcrest.core.Is.is; import static fj.data.List.list; diff --git a/core/src/test/java/fj/function/StringsTest.java b/core/src/test/java/fj/function/StringsTest.java index 79cb8a3d..db20fd68 100644 --- a/core/src/test/java/fj/function/StringsTest.java +++ b/core/src/test/java/fj/function/StringsTest.java @@ -5,8 +5,10 @@ import static fj.Function.compose; import static fj.function.Strings.*; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.*; import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.assertThat; + public class StringsTest { @Test diff --git a/demo/build.gradle b/demo/build.gradle index f19e765f..87bf7a5c 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -6,10 +6,10 @@ mainClassName = "fj.demo.euler.Problem2" archivesBaseName = "${project.projectName}-${project.name}" dependencies { - compile project(":core") - compile project(":quickcheck") - testCompile junitCompile - testRuntime junitRuntime + api project(":core") + api project(":quickcheck") + testImplementation junitCompile + testImplementation junitRuntime } test { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8cf6eb5a..fbce071a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/java-core/build.gradle b/java-core/build.gradle index e7b40bb9..f6a6146a 100644 --- a/java-core/build.gradle +++ b/java-core/build.gradle @@ -3,16 +3,15 @@ archivesBaseName = "${project.projectName}-${project.name}" ext { signModule = true + uploadModule = true } dependencies { - compile project(":core") - testCompile junitCompile - testRuntime junitRuntime + api project(":core") + testImplementation junitCompile + testRuntimeOnly junitRuntime } performSigning(signingEnabled, signModule) -configureUpload(signingEnabled, signModule) +configureUpload(signingEnabled, signModule, uploadModule) - -uploadArchives.enabled = true diff --git a/lib.gradle b/lib.gradle index 39ce7a7e..5ff91f01 100644 --- a/lib.gradle +++ b/lib.gradle @@ -20,7 +20,7 @@ String findJavaCommand(String command) { Boolean doSigning(String signingAllowed, Boolean doModule) { def b = signingAllowed.trim() == "true" && doModule -// println("signModule: ${project.name} signingEnabled: $signingAllowed module: $doModule") +// println("signModule: ${project.name} signingAllowed: $signingAllowed doModule: $doModule") b } @@ -31,50 +31,122 @@ void performSigning(String signingAllowed, Boolean doModule) { } } -void configureUpload(String signingEnabled, Boolean signModule) { +def customisePom(pom, gradleProject) { + pom.withXml { + def root = asNode() - uploadArchives { - enabled = false - repositories { - mavenDeployer { - if (doSigning(signingEnabled, signModule)) { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - } + // add all items necessary for maven central publication + root.children().last() + { + resolveStrategy = Closure.DELEGATE_FIRST - repository(url: sonatypeUploadUrl) { - authentication(userName: sonatypeUsername, password: sonatypePassword) + name project.pomProjectName + description project.projectDescription + url project.projectUrl + organization { + name project.pomOrganisation + url project.projectUrl + } + issueManagement { + system 'GitHub' + url project.issueUrl + } + licenses { + license { + name project.licenseName + url project.licenseUrl + distribution 'repo' } - pom { - groupId = project.group - project { - name pomProjectName - packaging 'jar' - description projectDescription - url projectUrl - organization { - name pomOrganisation - url projectUrl - } - scm { - url scmUrl - } - licenses { - license { - name "The BSD3 License" - url "https://github.com/functionaljava/functionaljava/blob/master/etc/LICENCE" - distribution 'repo' + } + scm { + url project.githubUrl + connection project.scmGitFile + developerConnection project.scmSshGitFile + } + } + } +} + + +void configureUpload(String signingEnabled, Boolean signModule, Boolean uploadModule) { + + if (uploadModule) { + + publishing { + + publications { + + mavenJava(MavenPublication) { + groupId project.group + artifactId project.name + version project.version + + from components.java + + customisePom(pom, rootProject) + + artifact sourcesJar + artifact javadocJar + + if (doSigning(signingEnabled, signModule)) { + // sign the pom + pom.withXml { + def pomFile = file("${project.buildDir}/generated-pom.xml.asc") + writeTo(pomFile) + def pomAscFile = signing.sign(pomFile).signatureFiles[0] + artifact(pomAscFile) { + classifier = null + extension = 'pom.asc' } + pomFile.delete() } - developers { - developer { - email primaryEmail + + // sign the artifacts + project.tasks.signArchives.signatureFiles.each { + artifact(it) { + def matcher = it.file =~ /-(sources|javadoc|jre8|jre9)\.jar\.asc$/ + if (matcher.find()) { + classifier = matcher.group(1) + } else { + classifier = null + } + extension = 'jar.asc' } } } + + } + + } + + repositories { + maven { + url project.sonatypeUploadUrl + credentials { + username sonatypeUsername + password sonatypePassword + } } } + + } + + model { + tasks.publishMavenJavaPublicationToMavenLocal { + dependsOn(project.tasks.signArchives) + } + tasks.publishMavenJavaPublicationToMavenRepository { + dependsOn(project.tasks.signArchives) + } + tasks.publish { + dependsOn(project.tasks.build) + } +// tasks.install { +// dependsOn(project.tasks.build) +// } } + } + } ext { diff --git a/performance/build.gradle b/performance/build.gradle index c6f4c8a6..7efbde2e 100644 --- a/performance/build.gradle +++ b/performance/build.gradle @@ -1,6 +1,6 @@ dependencies { - compile project(":core") - testCompile junitCompile - testRuntime junitRuntime + api project(":core") + testImplementation junitCompile + testRuntimeOnly junitRuntime } diff --git a/props-core-scalacheck/build.gradle b/props-core-scalacheck/build.gradle index f26b5425..358b365b 100644 --- a/props-core-scalacheck/build.gradle +++ b/props-core-scalacheck/build.gradle @@ -11,12 +11,12 @@ ext { } dependencies { - compile project(":core") - compile "org.scala-lang:scala-library:$scalaVersion" - compile "org.scalacheck:scalacheck_$scalacheckScalaVersion:$scalacheckVersion" + api project(":core") + api "org.scala-lang:scala-library:$scalaVersion" + api "org.scalacheck:scalacheck_$scalacheckScalaVersion:$scalacheckVersion" - testCompile junitCompile - testRuntime junitRuntime + testImplementation junitCompile + testRuntimeOnly junitRuntime } tasks.withType(ScalaCompile) { @@ -24,4 +24,4 @@ tasks.withType(ScalaCompile) { } performSigning(signingEnabled, signModule) -configureUpload(signingEnabled, signModule) +configureUpload(signingEnabled, signModule, project.uploadModule) diff --git a/props-core/build.gradle b/props-core/build.gradle index b991bd47..534a051b 100644 --- a/props-core/build.gradle +++ b/props-core/build.gradle @@ -2,7 +2,7 @@ archivesBaseName = "${project.projectName}-${project.name}" dependencies { - compile project(":quickcheck") - testCompile junitCompile - testRuntime junitRuntime + api project(":quickcheck") + testImplementation junitCompile + testRuntimeOnly junitRuntime } diff --git a/props-core/src/test/java/fj/data/ReaderTest.java b/props-core/src/test/java/fj/data/ReaderTest.java index 4817e41c..48352620 100644 --- a/props-core/src/test/java/fj/data/ReaderTest.java +++ b/props-core/src/test/java/fj/data/ReaderTest.java @@ -13,6 +13,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; + public class ReaderTest { @Test diff --git a/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java b/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java index 7f02288c..8cc94825 100644 --- a/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java +++ b/props-core/src/test/java/fj/data/fingertrees/FingerTreeTest.java @@ -8,10 +8,11 @@ import org.junit.Test; import static fj.Monoid.intAdditionMonoid; +import static fj.Monoid.intMinMonoid; import static fj.data.fingertrees.FingerTree.measured; import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.assertThat; public class FingerTreeTest { diff --git a/quickcheck/build.gradle b/quickcheck/build.gradle index 2e5dbe4c..c9851596 100644 --- a/quickcheck/build.gradle +++ b/quickcheck/build.gradle @@ -1,16 +1,15 @@ ext { signModule = true + uploadModule = true } archivesBaseName = "${project.projectName}-${project.name}" dependencies { - compile project(":core") - compile junitCompile + api project(":core") + api junitCompile } performSigning(signingEnabled, signModule) -configureUpload(signingEnabled, signModule) - -uploadArchives.enabled = true +configureUpload(signingEnabled, signModule, uploadModule) From 93c0403576becdffce91fdd9382b831725f6d795 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Thu, 17 Feb 2022 23:25:47 +1000 Subject: [PATCH 164/173] Removed deprecated jcenter. Added mavenLocal before mavenCentral in some missing cases. Fixed deprecated reports html enabled to required. --- build.gradle | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 9556b473..43e7ae5b 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ apply plugin: "com.github.ben-manes.versions" buildscript { repositories { mavenLocal() - jcenter() mavenCentral() + gradlePluginPortal() } dependencies { @@ -83,9 +83,8 @@ allprojects { } repositories { - jcenter() - mavenCentral() mavenLocal() + mavenCentral() } version = fjVersion @@ -98,6 +97,7 @@ subprojects { buildscript { repositories { + mavenLocal() mavenCentral() } } @@ -108,7 +108,6 @@ subprojects { repositories { mavenLocal() - jcenter() mavenCentral() maven { url sonatypeRepositoryUrl @@ -124,8 +123,8 @@ subprojects { tasks.withType(Test).configureEach { maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 if (!generateTestReports) { - reports.html.enabled = false - reports.junitXml.enabled = false + reports.html.required = false + reports.junitXml.required = false } } @@ -141,8 +140,8 @@ task coverage(type: org.gradle.testing.jacoco.tasks.JacocoReport) { getSourceDirectories().from(files(subprojects.findAll {subproject -> subproject.name in projectForFoverage} .sourceSets.main.allSource.srcDirs)) reports { - html.enabled = true - xml.enabled = true + html.required = true + xml.required = true } } From 0c7f70139917f46c1f855a7258678e6c1377bf0b Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Thu, 17 Feb 2022 23:39:00 +1000 Subject: [PATCH 165/173] Upgrade from Gradle 7.3 t o7.4 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 43e7ae5b..08204ba9 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { } wrapper { - gradleVersion = "7.3" + gradleVersion = "7.4" distributionType = Wrapper.DistributionType.ALL } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fbce071a..b1159fc5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 2ad5cd9f098b574f4e72d4f1924f6c0962621397 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 18 Feb 2022 00:25:11 +1000 Subject: [PATCH 166/173] Do some Travis optimisation --- .travis.yml | 6 +++++- gradle.properties | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bc3fc57f..2fb30bf7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,13 +14,17 @@ matrix: allow_failures: script: - - ./gradlew clean test + - ./gradlew build --no-daemon env: global: - secure: Bun+1FZ29Q3dR9gZ/5brxcSf+zcY5tWrsqOA4GUb5bYCMyORuXQB0FYXuhKR4wB1pFrk1a9EYwRwSu3GwRJVWb+UzF0CNOWF/QG5tGPx32IOXScwlL/KonI4Vhs7Oc0fF4Wdb7euNrT27BU61jbUugjJ642b3n0VBYFYDdquprU= - secure: QAxhjqLRa+WHKIzgIJPZ/rM5a5uzqG7E5rsC0YvB25cO712oYXmzsYPia/oSp0chXlYLYMfk2UnLeQCSx2e6ogXRRRa977Q+B33Nt0Hd9SGLtduv6DBrbA2ehLU12Ib4DWe5VhF5eueAunycYcllTvqA5h+pzTtEVbd68ZHncM4= +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ + cache: directories: - $HOME/.gradle/caches/ diff --git a/gradle.properties b/gradle.properties index 6ee9a768..e96743fd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,3 +9,4 @@ sonatypePassword = incorrectPwd #signing.secretKeyRingFile= org.gradle.parallel = true +org.gradle.caching = true From 1dd3b3e8aa84df1d7788d74bcc17457efbc2511f Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 18 Feb 2022 00:49:23 +1000 Subject: [PATCH 167/173] Update readme for FJ 5.0 --- README.adoc | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/README.adoc b/README.adoc index 4ede16b7..e24b3e6f 100644 --- a/README.adoc +++ b/README.adoc @@ -8,7 +8,7 @@ image::http://www.functionaljava.org/img/logo-600x144.png[] Functional Java is an open source library facilitating functional programming in Java. The library implements numerous basic and advanced programming abstractions that assist composition oriented development. Functional Java also serves as a platform for learning functional programming concepts by introducing these concepts using a familiar language. -The library is intended for use in production applications and is thoroughly tested using the technique of automated specification-based testing with ScalaCheck and Functional Java's quickcheck module. Functional Java is compiled with Java 8 targeting Java 7 bytecode. The use of lambdas within the project are back ported with the Retro Lambda library, supporting Java versions 6 to 8. +The library is intended for use in production applications and is thoroughly tested using the technique of automated specification-based testing with ScalaCheck and Functional Java's quickcheck module. Functional Java provides abstractions for the following types: @@ -32,16 +32,13 @@ The recommended way to download and use the project is through your build tool. The Functional Java artifact is published to Maven Central using the group `org.functionaljava` with three published artifacts: -* the core library (`functionaljava` or `functionaljava_1.8` if you use Java 8+) -* Java 8 specific support (`functionaljava-java8`) -* property based testing (`functionaljava-quickcheck` or `functionaljava-quickcheck_1.8` if you use Java 8+) +* the core library (`functionaljava`) +* property based testing (`functionaljava-quickcheck`) -The latest stable version is `4.9`. This can be added to your Gradle project by adding the dependencies: +The latest stable version is `5.0`. This can be added to your Gradle project by adding the dependencies: ---- -compile "org.functionaljava:functionaljava:4.9" -compile "org.functionaljava:functionaljava-java8:4.9" -compile "org.functionaljava:functionaljava-quickcheck:4.9" -compile "org.functionaljava:functionaljava-java-core:4.9" +compile "org.functionaljava:functionaljava:5.0" +compile "org.functionaljava:functionaljava-quickcheck:5.0" ---- and in Maven: @@ -49,34 +46,22 @@ and in Maven: org.functionaljava functionaljava - 4.9 - - - org.functionaljava - functionaljava-java8 - 4.9 + 5.0 org.functionaljava functionaljava-quickcheck - 4.9 - - - org.functionaljava - functionaljava-java-core - 4.9 + 5.0 ---- == Building -FunctionalJava uses the Retro Lambda project to backport Java 8 lambdas to Java 6 bytecode. This requires access to both JDK 6 and 8. The build system requires the environment variables `JAVA6_HOME` and `JAVA8_HOME` to refer to the appropriate directories. - -Building is done using Gradle 6.8.3. In the root directory run: +Building is done using Gradle 7.4. In the root directory run: ---- ./gradlew ---- -This requires access to Java and will download the Gradle build tool and necessary dependencies and build FunctionalJava. +This requires access to Java 8 and will download the Gradle build tool and necessary dependencies and build FunctionalJava. == Features @@ -126,4 +111,4 @@ link:etc/LICENCE[The Functional Java license] uses the BSD 3 license (3-clause l == Release Notes -For release notes for each version, see the directory etc/release-notes. \ No newline at end of file +For release notes for each version, see the directory etc/release-notes. From 1bcfaa1cb1e4a2e1db710984dc3a1ebd374a09c1 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 18 Feb 2022 23:48:22 +1000 Subject: [PATCH 168/173] Updated gradle versions plugin to 0.42.0. Added arb values for ParModule to help with upgrading Scala and Scalacheck --- build.gradle | 2 +- .../src/main/java/fj/test/Arbitrary.java | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 08204ba9..c89c9536 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { } dependencies { - classpath "com.github.ben-manes:gradle-versions-plugin:0.36.0" + classpath "com.github.ben-manes:gradle-versions-plugin:0.42.0" classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:6.1.0" } diff --git a/quickcheck/src/main/java/fj/test/Arbitrary.java b/quickcheck/src/main/java/fj/test/Arbitrary.java index 146d0a9b..04f1e20b 100644 --- a/quickcheck/src/main/java/fj/test/Arbitrary.java +++ b/quickcheck/src/main/java/fj/test/Arbitrary.java @@ -23,6 +23,9 @@ import fj.P6; import fj.P7; import fj.P8; +import fj.Unit; +import fj.control.parallel.ParModule; +import fj.control.parallel.Strategy; import fj.data.*; import fj.LcgRng; import fj.Ord; @@ -65,9 +68,12 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadFactory; /** * Common Gen helper functions. @@ -98,6 +104,24 @@ public static Gen> arbState(Gen as, Cogen cs, Gen aa return arbF(cs, arbP2(as, aa)).map(State::unit); } + public static Gen arbParModule() { + return Arbitrary.arbStrategy().map(s -> ParModule.parModule(s)); + } + + public static Gen> arbStrategy() { + Strategy s = Strategy.executorStrategy(fixedThreadsExecutorService(2)); + return Gen.elements(s); + } + + private static ExecutorService fixedThreadsExecutorService(int n) { + return Executors.newFixedThreadPool(n, r -> { + ThreadFactory tf = Executors.defaultThreadFactory(); + Thread t = tf.newThread(r); + t.setDaemon(true); + return t; + }); + } + /** * An arbitrary for the LcgRng. */ From aad325739af7dae57c921506a66ee8e4f6ddd026 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 18 Feb 2022 23:50:55 +1000 Subject: [PATCH 169/173] Added problematic upgrade tests from CheckParModule.scala to CheckParModuleTest.java --- .../control/parallel/CheckParModuleTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 props-core/src/test/java/fj/control/parallel/CheckParModuleTest.java diff --git a/props-core/src/test/java/fj/control/parallel/CheckParModuleTest.java b/props-core/src/test/java/fj/control/parallel/CheckParModuleTest.java new file mode 100644 index 00000000..dfbbdda5 --- /dev/null +++ b/props-core/src/test/java/fj/control/parallel/CheckParModuleTest.java @@ -0,0 +1,48 @@ +package fj.control.parallel; + +import fj.Equal; +import fj.F; +import fj.Monoid; +import fj.P; +import fj.P1; +import fj.P2; +import fj.data.Stream; +import fj.test.Arbitrary; +import fj.test.Property; +import fj.test.runner.PropertyTestRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static fj.Equal.stringEqual; +import static fj.Monoid.stringMonoid; +import static fj.test.Arbitrary.arbInteger; +import static fj.test.Arbitrary.arbP1; +import static fj.test.Arbitrary.arbParModule; +import static fj.test.Arbitrary.arbStream; +import static fj.test.Arbitrary.arbString; +import static fj.test.Property.prop; +import static fj.test.Property.property; + +@RunWith(PropertyTestRunner.class) +public class CheckParModuleTest { + + public Property parFlatMap() { + return property(arbStream(arbString), arbParModule(), (str, pm) -> { + F> f = s3 -> Stream.stream(s3, reverse().f(s3)); + return prop(Equal.streamEqual(stringEqual).eq(str.bind(f), pm.parFlatMap(str, f).claim())); + }); + } + + private F reverse() { + return s2 -> new StringBuilder(s2).reverse().toString(); + } + + public Property parFoldMap() { + return property(arbStream(arbString), arbParModule(), (str, pm) -> { + F, P2, Stream>> chunk = x -> P.p(Stream.stream(x.head()), x.tail()._1()); + return prop(stringEqual.eq(stringMonoid.sumLeft(str.map(reverse())), pm.parFoldMap(str, reverse(), stringMonoid, chunk).claim())); + }); + } + + +} From 3bced817e36f54c1191f378d9fc9380fb477b567 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Fri, 18 Feb 2022 23:59:51 +1000 Subject: [PATCH 170/173] Removed problematic properties for Scala 2.12 that were moved to Java. --- .../test/scala/fj/control/parallel/CheckParModule.scala | 7 ------- 1 file changed, 7 deletions(-) diff --git a/props-core-scalacheck/src/test/scala/fj/control/parallel/CheckParModule.scala b/props-core-scalacheck/src/test/scala/fj/control/parallel/CheckParModule.scala index 8e13eb1b..bba1feff 100644 --- a/props-core-scalacheck/src/test/scala/fj/control/parallel/CheckParModule.scala +++ b/props-core-scalacheck/src/test/scala/fj/control/parallel/CheckParModule.scala @@ -46,11 +46,4 @@ object CheckParModule extends Properties("ParModule") { property("parMapArray") = forAll((s: Array[String], p: ParModule) => arrayEqual(stringEqual).eq(s.map(rev), p.parMap(s, rev).claim)) - property("parFlatMap") = forAll((s: Stream[String], p: ParModule) => { - val f = (x: String) => Stream.stream(x, rev(x)) : Stream[String] - streamEqual(stringEqual).eq(s.bind(f), p.parFlatMap(s, f).claim)}) - - property("parFoldMap") = forAll((s: Stream[String], p: ParModule) => { - val chunk = (x: Stream[String]) => P.p(Stream.stream(x.head), x.tail._1) - stringEqual.eq(stringMonoid.sumLeft(s.map(rev)), p.parFoldMap(s, rev, stringMonoid, chunk).claim)}) } From 835ca0fb9ae5278aa3371dfdf73562002747c72c Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Sat, 19 Feb 2022 01:02:47 +1000 Subject: [PATCH 171/173] Upgrade to Gradle 2.12. Removed CheckHashMap no null values test as the semantics changed with Scala 2.12 so that null.asInstanceOf[Int] returns 0 instead of null --- core/src/main/java/fj/function/Strings.java | 4 ++++ props-core-scalacheck/build.gradle | 12 ++++++++++-- .../src/test/scala/fj/data/CheckArray.scala | 4 ++-- .../src/test/scala/fj/data/CheckHashMap.scala | 11 ----------- .../java/fj/control/parallel/CheckParModuleTest.java | 12 ++++++------ 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/fj/function/Strings.java b/core/src/main/java/fj/function/Strings.java index 1c239e85..d9815248 100644 --- a/core/src/main/java/fj/function/Strings.java +++ b/core/src/main/java/fj/function/Strings.java @@ -96,4 +96,8 @@ public static F, String> unlines() { return Strings::unlines; } + public static F reverse() { + return s -> new StringBuilder(s).reverse().toString(); + } + } diff --git a/props-core-scalacheck/build.gradle b/props-core-scalacheck/build.gradle index 358b365b..faa7d737 100644 --- a/props-core-scalacheck/build.gradle +++ b/props-core-scalacheck/build.gradle @@ -4,9 +4,17 @@ archivesBaseName = "${project.projectName}-${project.name}" apply plugin: 'scala' ext { - scalaVersion = "2.11.12" - scalacheckScalaVersion = "2.11" +// scalaVersion = "2.11.12" +// scalacheckScalaVersion = "2.11" + scalaVersion = "2.12.15" + scalacheckScalaVersion = "2.12" +// scalaVersion = "2.13.8" +// scalacheckScalaVersion = "2.13" + scalacheckVersion = "1.12.6" +// scalacheckVersion = "1.13.5" +// scalacheckVersion = "1.15.2" + signModule = true } diff --git a/props-core-scalacheck/src/test/scala/fj/data/CheckArray.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckArray.scala index f8cce111..b116a6a9 100755 --- a/props-core-scalacheck/src/test/scala/fj/data/CheckArray.scala +++ b/props-core-scalacheck/src/test/scala/fj/data/CheckArray.scala @@ -63,7 +63,7 @@ object CheckArray extends Properties("Array") { a.reverse.foldRight((a: String, b: Array[String]) => array[String](scala.Array(a): _*).append(b), empty[String]))) property("scans") = forAll((a: Array[Int], z: Int) => { - val add = (x: Int, y: Int) => x + y + val add: F2[Int, Int, Int] = (x: Int, y: Int) => x + y val left = a.scanLeft(add, z) val right = a.reverse().scanRight(add, z).reverse() @@ -72,7 +72,7 @@ object CheckArray extends Properties("Array") { property("scans1") = forAll((a: Array[Int]) => (a.length() > 0) ==> { - val add = (x: Int, y: Int) => x + y + val add: F2[Int, Int, Int] = (x: Int, y: Int) => x + y val left = a.scanLeft1(add) val right = a.reverse().scanRight1(add).reverse() diff --git a/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala index dfde81e3..6ebd060d 100755 --- a/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala +++ b/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala @@ -105,15 +105,4 @@ object CheckHashMap extends Properties("HashMap") { keysAreEqual && valuesAreEqual }) - property("No null values") = forAll((list: List[Int]) => { - val m = HashMap.hashMap[Int, Int]() - list.foreachDoEffect(new Effect1[Int] { - def f(a: Int) { - m.set(a, null.asInstanceOf[Int]) - } - }) - list.forall(new F[Int, java.lang.Boolean]() { - def f(a: Int) = m.contains(a) == false - }) - }) } \ No newline at end of file diff --git a/props-core/src/test/java/fj/control/parallel/CheckParModuleTest.java b/props-core/src/test/java/fj/control/parallel/CheckParModuleTest.java index dfbbdda5..0c4f418f 100644 --- a/props-core/src/test/java/fj/control/parallel/CheckParModuleTest.java +++ b/props-core/src/test/java/fj/control/parallel/CheckParModuleTest.java @@ -7,6 +7,7 @@ import fj.P1; import fj.P2; import fj.data.Stream; +import fj.function.Strings; import fj.test.Arbitrary; import fj.test.Property; import fj.test.runner.PropertyTestRunner; @@ -28,19 +29,18 @@ public class CheckParModuleTest { public Property parFlatMap() { return property(arbStream(arbString), arbParModule(), (str, pm) -> { - F> f = s3 -> Stream.stream(s3, reverse().f(s3)); + F> f = s3 -> Stream.stream(s3, Strings.reverse().f(s3)); return prop(Equal.streamEqual(stringEqual).eq(str.bind(f), pm.parFlatMap(str, f).claim())); }); } - private F reverse() { - return s2 -> new StringBuilder(s2).reverse().toString(); - } - public Property parFoldMap() { return property(arbStream(arbString), arbParModule(), (str, pm) -> { F, P2, Stream>> chunk = x -> P.p(Stream.stream(x.head()), x.tail()._1()); - return prop(stringEqual.eq(stringMonoid.sumLeft(str.map(reverse())), pm.parFoldMap(str, reverse(), stringMonoid, chunk).claim())); + return prop(stringEqual.eq( + stringMonoid.sumLeft(str.map(Strings.reverse())), + pm.parFoldMap(str, Strings.reverse(), stringMonoid, chunk).claim() + )); }); } From 4671564744bd18e6d9c77ff03f47d8e3900cf28f Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Sat, 19 Feb 2022 01:22:40 +1000 Subject: [PATCH 172/173] Prep for next Scala version --- props-core-scalacheck/build.gradle | 1 + props-core-scalacheck/src/test/scala/fj/Tests.scala | 2 +- props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/props-core-scalacheck/build.gradle b/props-core-scalacheck/build.gradle index faa7d737..396d19d4 100644 --- a/props-core-scalacheck/build.gradle +++ b/props-core-scalacheck/build.gradle @@ -13,6 +13,7 @@ ext { scalacheckVersion = "1.12.6" // scalacheckVersion = "1.13.5" +// scalacheckVersion = "1.14.0" // scalacheckVersion = "1.15.2" signModule = true diff --git a/props-core-scalacheck/src/test/scala/fj/Tests.scala b/props-core-scalacheck/src/test/scala/fj/Tests.scala index d94661f9..c5ec3977 100644 --- a/props-core-scalacheck/src/test/scala/fj/Tests.scala +++ b/props-core-scalacheck/src/test/scala/fj/Tests.scala @@ -18,7 +18,7 @@ object Tests { fj.control.parallel.CheckParModule.properties ).flatten - def main(args: Array[String]) { + def main(args: Array[String]): scala.Unit = { run(tests) // System.exit(0) } diff --git a/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala b/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala index 6ebd060d..c629a10f 100755 --- a/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala +++ b/props-core-scalacheck/src/test/scala/fj/data/CheckHashMap.scala @@ -9,6 +9,8 @@ import Hash._ import Ord._ import fj.data.Option._ import scala.collection.JavaConversions._ +//import scala.collection.JavaConverters._ + import org.scalacheck.{Arbitrary, Properties} import data.ArbitraryList._ import org.scalacheck.Arbitrary._ From a2f7ebe5ef319df492f547e0be4cc965af667e35 Mon Sep 17 00:00:00 2001 From: Mark Perry Date: Mon, 7 Mar 2022 18:38:31 +1000 Subject: [PATCH 173/173] Updated doc when releasing FJ 5.0 --- README.adoc | 11 ++++++++-- etc/release-notes/release-notes-5.1.adoc | 28 ++++++++++++++++++++++++ etc/release-process.txt | 24 ++++++++++++++++---- 3 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 etc/release-notes/release-notes-5.1.adoc diff --git a/README.adoc b/README.adoc index e24b3e6f..ecf403e2 100644 --- a/README.adoc +++ b/README.adoc @@ -34,11 +34,13 @@ The Functional Java artifact is published to Maven Central using the group `org. * the core library (`functionaljava`) * property based testing (`functionaljava-quickcheck`) +* a small amount of Java 8 support (`functionaljava-java-core`) The latest stable version is `5.0`. This can be added to your Gradle project by adding the dependencies: ---- compile "org.functionaljava:functionaljava:5.0" compile "org.functionaljava:functionaljava-quickcheck:5.0" +compile "org.functionaljava:functionaljava-java-core:5.0" ---- and in Maven: @@ -53,11 +55,16 @@ and in Maven: functionaljava-quickcheck 5.0 + + org.functionaljava + functionaljava-java-core + 5.0 + ---- == Building -Building is done using Gradle 7.4. In the root directory run: +Building is done using Java 8 and Gradle 7.4. In the root directory run: ---- ./gradlew ---- @@ -111,4 +118,4 @@ link:etc/LICENCE[The Functional Java license] uses the BSD 3 license (3-clause l == Release Notes -For release notes for each version, see the directory etc/release-notes. +For release notes for each version, see the directory link:etc/release-notes. diff --git a/etc/release-notes/release-notes-5.1.adoc b/etc/release-notes/release-notes-5.1.adoc new file mode 100644 index 00000000..22dbc552 --- /dev/null +++ b/etc/release-notes/release-notes-5.1.adoc @@ -0,0 +1,28 @@ + += Release + +Proposed release: + +== Enhancements + +* TODO. + +== Fixes + +* TODO. + +== Internal + +* TODO. + +== Breaking Changes + +* TODO. + +== Documentation + +* TODO. + +== Contributors + +* TODO. diff --git a/etc/release-process.txt b/etc/release-process.txt index e4b35e2d..f5699a5b 100644 --- a/etc/release-process.txt +++ b/etc/release-process.txt @@ -1,6 +1,6 @@ -Current Release Process -======================= +Release Process +=============== Go through the issues and pull requests and set the Label and Milestone field. Add information to /etc/release-notes/release-notes-.adoc. @@ -10,7 +10,7 @@ Update gradle.properties: Run the build command: -gradlew clean build upload +gradlew clean build publishAllPublicationsToMavenRepository Login to Sonatype and verify the release: * Login to https://oss.sonatype.org @@ -18,6 +18,7 @@ Login to Sonatype and verify the release: * Tick the release and click Close * Wait until closed * Tick the release and click Release +* It takes ~24 hours for Sonatype to sync to Maven Central where it should appear Commit changes @@ -33,12 +34,27 @@ Create tag: Create the next version of the release notes with empty fields using the template. -Copy the generated javadoc for each component to the website repositories' master branch under /javadoc/. Commit the javadoc and push. +Copy the generated javadoc for each component to the website repositories' master branch, see https://github.com/functionaljava/functionaljava.github.io, under /javadoc/. Copy: +- core to functionaljava +- java-core to functionaljava-java-core +- quickcheck to functionaljava-quickcheck + +Commit the javadoc and push. Update the website and Github README.adoc. This includes adding any features to the home page and features page. Updating the doc page with javadoc links. Update the download page with a link to the latest release notes. Send a message to the group and social media about the release, TODO. +Setup Sonatype +====================== +You need to be able to login to https://oss.sonatype.org. Register or check your login details. + +Create/edit the file %UserProfile%\.gradle\gradle.properties, set the values below: + +sonatypeUsername = +sonatypePassword = + + Setup Artifact Signing ====================== The below text is a summary from https://gist.github.com/phit/bd3c6d156a2fa5f3b1bc15fa94b3256c.